import inspect
import logging
import logging.handlers
import sys
from os import environ 
import http
from loguru import logger
from typing import Literal



LogLevelCodes = Literal[
        "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "OFF"
    ]

def log(msg):
    print(msg)

def initialise_logger(program="AvenirHealth", env="development", save_to_file=False):
    try:    
        global log_level
        log_level = environ.get("AW_SW_LOG_LEVEL", "WARNING")#Default to ERROR as not to overwhelm PT
        
        global log_program
        log_program = program
        
        global log_env
        log_env = env
        
        intercept_logs()
        
        
        # replace default sysout logger
        logger.remove()
        log_stdout_id = logger.add(
            sys.stdout,
            level=log_level,
            enqueue=True,
        )
        
        global papertrail_log_id
        papertrail_log_id = None
        if env!="development":
            set_pt_log_level(log_level)
        
        if save_to_file:
            logger.add(
                "debug.log",
                rotation="20 days",
                retention="1 months",
                enqueue=True,
            )
            
        logger.warning(f"Logger initialised for {program} ({env})")
        return logger
    except Exception as e:
        print(f'Failed starting logger: {e}')
        return None

def get_pt_log_level():
    global log_level
    return log_level
    

def set_pt_log_level(level):
    try:
        global papertrail_log_id
        global log_program
        global log_env
        global log_level
        log_level = level
        
        if papertrail_log_id:
            logger.remove(papertrail_log_id)
            papertrail_log_id = None
        if level!='OFF':
            pt_url, pt_port = environ.get('AVENIR_SW_PAPERTRAIL_LOG', ':').split(':') 
            
            papertrail_handler = logging.handlers.SysLogHandler(
                address=(pt_url, int(pt_port))
            )
            papertrail_format = "1 - - " + log_program + " " + log_env + " - -| <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>"
            papertrail_log_id = logger.add(
                    papertrail_handler,
                    format=papertrail_format,
                    level=level,
                    colorize=True,
                    backtrace=False,
                    diagnose=False,
                    enqueue=True,
            )
        result = f'Log level is {level} for {log_program} ({log_env})'
        logger.warning(result)
        return result
    except Exception as e:
        result = 'Failed to set log in papertrail'
        print(f'Failed to set log in papertrail: {e}')
        if logger:
            logger.critical(result)
        return result

def intercept_logs():
    logging.basicConfig(handlers=[InterceptHandler()], force=True)
    # logging.getLogger("uvicorn.access").handlers = [InterceptHandler()]
    # logging.getLogger("fastapi").handlers = [InterceptHandler()]

    # these logs are already being  caught by the basicConfig interceptor
    logging.getLogger("uvicorn").handlers = []
    logging.getLogger("uvicorn.error").handlers = []


def reformat_requests(record: logging.LogRecord) -> str:
    if len(record.args) == 5 and record.args[1] in [
        "GET",
        "POST",
        "PUT",
        "DELETE",
        "PATCH",
    ]:
        (
            client_addr,
            method,
            full_path,
            http_version,
            status_code,
        ) = record.args

        try:
            status_phrase = http.HTTPStatus(int(status_code)).phrase
        except ValueError:
            status_phrase = "Unknown status"

        match status_code // 100:
            case 1:
                new_levelname = "INFO"
                new_levelno = 20
            case 2:
                new_levelname = "INFO"
                new_levelno = 20
            case 3:
                new_levelname = "INFO"
                new_levelno = 20
            case 4:
                new_levelname = "WARNING"
                new_levelno = 30
            case 5:
                new_levelname = "ERROR"
                new_levelno = 40
            case _:
                new_levelname = "INFO"
                new_levelno = 20

        new_message = record.msg + " %s"
        new_args = (
            client_addr,
            method,
            full_path,
            http_version,
            status_code,
            status_phrase,
        )

        record.levelname = new_levelname
        record.levelno = new_levelno
        record.msg = new_message
        record.args = new_args
    return record


class InterceptHandler(logging.Handler):
    def emit(self, record: logging.LogRecord) -> None:
        if record.levelname != 'INFO':
            record = reformat_requests(record)

            level: str | int
            try:
                level = logger.level(record.levelname).name
            except ValueError:
                level = record.levelno

            frame, depth = inspect.currentframe(), 0
            while frame and (depth == 0 or frame.f_code.co_filename == logging.__file__):
                frame = frame.f_back
                depth += 1

            logger.opt(depth=depth, exception=record.exc_info).log(
                level, record.getMessage()
            )
