from datetime import datetime
import json
import re
from typing import TypeAlias

from rich.highlighter import JSONHighlighter
from rich.text import Text

from .highlighter import LogHighlighter
from toolong import timestamps

ParseResult: TypeAlias = tuple[datetime | None, str, Text]


class LogFormat:
    def parse(self, line: str) -> ParseResult | None:
        raise NotImplementedError()


class RegexLogFormat(LogFormat):
    REGEX = re.compile(".*?")
    HIGHLIGHT_WORDS = [
        "GET",
        "POST",
        "PUT",
        "HEAD",
        "POST",
        "DELETE",
        "OPTIONS",
        "PATCH",
    ]

    highlighter = LogHighlighter()

    def parse(self, line: str) -> ParseResult | None:
        match = self.REGEX.fullmatch(line)
        if match is None:
            return None
        groups = match.groupdict()
        _, timestamp = timestamps.parse(groups["date"].strip("[]"))

        text = self.highlighter(line)
        if status := groups.get("status", None):
            text.highlight_words(
                [f" {status} "], "bold red" if status.startswith("4") else "magenta"
            )
        text.highlight_words(self.HIGHLIGHT_WORDS, "bold yellow")

        return timestamp, line, text


class CommonLogFormat(RegexLogFormat):
    REGEX = re.compile(
        r'(?P<ip>.*?) (?P<remote_log_name>.*?) (?P<userid>.*?) (?P<date>\[.*?(?= ).*?\]) "(?P<request_method>.*?) (?P<path>.*?)(?P<request_version> HTTP\/.*)?" (?P<status>.*?) (?P<length>.*?) "(?P<referrer>.*?)"'
    )


class CombinedLogFormat(RegexLogFormat):
    REGEX = re.compile(
        r'(?P<ip>.*?) (?P<remote_log_name>.*?) (?P<userid>.*?) \[(?P<date>.*?)(?= ) (?P<timezone>.*?)\] "(?P<request_method>.*?) (?P<path>.*?)(?P<request_version> HTTP\/.*)?" (?P<status>.*?) (?P<length>.*?) "(?P<referrer>.*?)" "(?P<user_agent>.*?)" (?P<session_id>.*?) (?P<generation_time_micro>.*?) (?P<virtual_host>.*)'
    )


class DefaultLogFormat(LogFormat):
    highlighter = LogHighlighter()

    def parse(self, line: str) -> ParseResult | None:
        text = self.highlighter(line)
        return None, line, text


class JSONLogFormat(LogFormat):

    highlighter = JSONHighlighter()

    def parse(self, line: str) -> ParseResult | None:
        line = line.strip()
        if not line:
            return None
        try:
            json.loads(line)
        except Exception:
            return None
        _, timestamp = timestamps.parse(line)
        text = self.highlighter(line)
        return timestamp, line, text


FORMATS = [
    JSONLogFormat(),
    CommonLogFormat(),
    CombinedLogFormat(),
    DefaultLogFormat(),
]


class FormatParser:
    """Parses a log line."""

    def __init__(self) -> None:
        self._formats = FORMATS.copy()

    def parse(self, line: str) -> ParseResult:
        """Parse a line."""
        for index, format in enumerate(self._formats):
            parse_result = format.parse(line)
            if parse_result is not None:
                if index:
                    del self._formats[index : index + 1]
                    self._formats.insert(0, format)
                return parse_result
        return None, "", Text()
