# Source code for noctua.utils.check

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-

    """Collection of decorators to check several type of errors in
    specific types of functions.

    """

    # System modules
    import socket

    # Third-party modules
    import requests
    from pyvantagepro.device import NoDeviceException

    # Other templates
    from ..utils.logger import log

    # class ManualRetry(Exception):
    #     '''
    #     Manually raise an exception, so it is possible to handle a retry
    #     '''

    def content_errors(content):
        '''
        Decorator for handling exceptions.
        Put it on top of the content() method
        in the shinsapp class.
        '''

        def content_errors_inner(*args, **kwargs):
            '''
            Modifies the content() function
            '''

            this = args[0]  # It's the "self"

            try:
                '''
                This is the content method in basetemplate
                which is implemented in each template.
                If all goes well, it is executed smoothly.
                '''
                content(*args, **kwargs)

            except Exception as e:
                '''
                If an error occurs, the template is paused at the
                beginning of the following paragraph.
                '''

                log.error(f"CONTENT: exception {e}")
                this.paused = True

        return content_errors_inner

    def request_errors(func):
        '''
        Decorator for handling exceptions on a class method.
        Put it on top of the class method.
        '''
        def request_errors_inner(*args, **kwargs):
            '''
            Modifies the func() method.
            '''

            this = args[0]  # it's the "self"
            name = str(args[0].__class__)

            try:
                this.error = []
                res = func(*args, **kwargs)
                return res

            except requests.exceptions.HTTPError as e:
                res = e.response

                if res.status_code == 400:
                    # API returns body like: '0x8000100a\r\nParameter(s) missing.\r\n'
                    try:
                        # Attempt to parse the API-specific error
                        body_lines = res.text.strip().split('\r\n')
                        error_code = body_lines[0]
                        error_message = body_lines[1] if len(body_lines) > 1 else "No error message provided."
                        msg = f"{name}: Bad Request - API Error {error_code}: {error_message}"
                        log.error(msg)
                        this.error.append(msg)
                    except (IndexError, AttributeError):
                        # Fallback for unexpected 400 error format
                        msg = f"{name}: Bad Request with unparsable body."
                        log.error(msg)
                        log.error(f"Raw response: {res.text}")
                        this.error.append(msg)

                elif res.status_code == 500:
                    msg = f"{name}: Internal Server Error on device."
                    log.error(msg)
                    log.error(f"Reason: {res.reason}")
                    log.error(f"Response: {res.text}")
                    this.error.append(msg)
                    this.error.append(res.text)

                else:  # 404 Not Found, etc.
                    msg = f"{name}: HTTP Error - {e}"
                    log.error(msg)
                    this.error.append(str(e))

                return

            except (requests.exceptions.Timeout, requests.exceptions.ConnectTimeout) as e:
                msg = f"{name}: Timeout or Connection timeout!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return []

            except requests.exceptions.TooManyRedirects as e:
                msg = f"{name}: Bad request!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return

            except ConnectionRefusedError as e:
                msg = f"{name}: Connection refused!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return

            except requests.exceptions.ConnectionError as e:
                msg = f"{name}: Connection error!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return []

            except AttributeError as e:
                msg = f"{name}: No data!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return

            except AssertionError as e:
                msg = f"{name}: Probably cabinet not ready!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return

            except TypeError as e:
                msg = f"{name}: Probably subsystem not responding!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return

            except requests.exceptions.RequestException as e:
                msg = f"{name}: Generic request exception!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                raise SystemExit(e) from e

            except Exception as e:
                msg = f"{name}: Not handled error!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                raise SystemExit(e) from e

        return request_errors_inner

    def telnet_errors(func):
        '''
        Decorator for handling exceptions on a class method.
        Put it on top of the class method.
        '''
        def telnet_errors_inner(*args, **kwargs):
            '''
            Modifies the func() method.
            '''

            this = args[0]  # it's the "self"
            name = str(args[0].__class__)

            try:
                this.error = []
                return func(*args, **kwargs)

            except socket.gaierror as e:
                msg = f"{name}: Server not found!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return

            except socket.timeout as e:
                msg = f"{name}: Server timeout!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return

            except AttributeError as e:
                msg = f"No attribute with this name in {name}"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return

            except BrokenPipeError as e:
                msg = f"{name}: Probably sent request to telnet while waiting previous response"
                log.debug(msg)
                log.debug(e)
                log.error(e)

            except IOError as e:
                msg = f"{name}: Probably failed getting info from telnet"
                log.debug(msg)
                log.debug(e)
                log.error(e)

            except Exception as e:
                msg = f"{name}: Not handled error"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                raise SystemExit(e)

        return telnet_errors_inner

    def ascom_errors(func):
        '''
        Decorator for handling exceptions on a class method.
        Put it on top of the class method.
        '''
        def ascom_errors_inner(*args, **kwargs):
            '''
            Modifies the func() method.
            '''

            this = args[0]  # it's the "self"
            name = str(args[0].__class__)

            try:
                this.error = []
                return func(*args, **kwargs)

            except Exception as e:
                msg = f"{name}: Not handled error"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                raise SystemExit(e)

        return ascom_errors_inner

    def meteo_errors(func):
        '''
        Decorator for handling exceptions on a class method.
        Put it on top of the class method.
        '''
        def meteo_errors_inner(*args, **kwargs):
            '''
            Modifies the func() method.
            '''

            this = args[0]  # it's the "self"
            name = str(args[0].__class__)

            try:
                this.error = []
                return func(*args, **kwargs)

            except NoDeviceException as e:
                msg = f"{name}: Cannot connect to meteo station"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))

            except BrokenPipeError as e:
                msg = f"{name}: Probably sent request to meteo station while waiting previous response"
                log.debug(msg)
                log.debug(e)

            except IOError as e:
                msg = f"{name}: Probably failed getting info from Meteo station"
                log.debug(msg)
                log.debug(e)

            except Exception as e:
                msg = f"{name}: Not handled error"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                raise SystemExit(e)

        return meteo_errors_inner

    def socket_errors(func):
        '''
        Decorator for handling exceptions on a class method.
        Put it on top of the class method.
        '''
        def socket_errors_inner(*args, **kwargs):
            '''
            Modifies the func() method.
            '''

            this = args[0]  # it's the "self"
            name = str(args[0].__class__)

            try:
                this.error = []
                return func(*args, **kwargs)

            except Exception as e:
                msg = f"{name}: Not handled exception!"
                log.error(msg)
                log.error(e)
                this.error.append(msg)
                this.error.append(str(e))
                return