import base64
import re
import socket
from email.message import Message
from email.parser import HeaderParser
from typing import Any, Literal

import msgspec
import xmltodict

from csirtmu.beta.config import BASE_DIR
from csirtmu.beta_temporal.external_services.rt import structs


class CopyrightDetails(msgspec.Struct):
    complainant_name: str | None = None
    complainant_email: str | None = None
    culprit_ip: str | None = None
    port: int | None = None
    infringing_time: str | None = None
    infringing_file: str | None = None


def parse_headers(headers: str) -> Message:
    parser = HeaderParser()
    return parser.parsestr(headers)


def get_header(headers: Message, name: str) -> list[str]:
    value = headers.get(name, None)
    if isinstance(value, str):
        return [value]
    return value or []


def search_for_keyword_in_attachments(attachments: list[structs.AttachmentObjectFromAPI], keyword: str) -> bool:
    return any(
        (attachment.content_type and keyword in attachment.content_type)
        or (attachment.filename and keyword in attachment.filename)
        for attachment in attachments
    )


def search_for_keyword_in_header(headers: Message, header: str, keyword: str) -> bool:
    if header in headers:
        if isinstance(value := headers[header], str):
            return bool(re.search(re.escape(keyword), value, re.IGNORECASE))
        if isinstance(value, list):
            for val in value:
                val: Any
                if isinstance(val, str) and re.search(re.escape(keyword), val, re.IGNORECASE):
                    return True
    return False


def search_for_keyword_non_quoted(text: str, keyword: str) -> bool:
    if "<blockquote>" in text and "</blockquote>" in text:
        start_index = text.find("<blockquote>")
        end_index = text.find("</blockquote>")
        return search_for_keyword_non_quoted(text[:start_index] + text[end_index + 14 :], keyword)
    return keyword in text


def extract_xml_from_body(body: str) -> dict[str, Any]:
    result: list[str] = []
    xml = False
    for line in body.split("\n"):
        if "Start ACNS XML" in line:
            xml = True
            continue
        if "End ACNS XML" in line:
            xml = False
        if xml and line.strip() != "":
            result.append(line)
    return xmltodict.parse("\n".join(result))


def parse_xml_from_attachment(attachments: list[structs.AttachmentObjectFromAPI]) -> dict[str, Any]:
    for attachment in attachments:
        if (attachment.filename is not None and "xml" in attachment.filename) or ("xml" in attachment.content_type):
            return xmltodict.parse(base64.b64decode(attachment.content or "").decode("utf-8"))
    return {}


def get_details_from_xml(xml: dict[str, Any]) -> CopyrightDetails:
    details = CopyrightDetails()
    details.complainant_name = xml.get("Complainant", {}).get("Entity")
    details.complainant_email = xml.get("Complainant", {}).get("Email")
    details.culprit_ip = xml.get("Source", {}).get("IP_Address")
    details.port = xml.get("Source", {}).get("Port")
    details.infringing_time = xml.get("Source", {}).get("TimeStamp")
    item_or_items: dict[str, Any] | list[dict[str, Any]] = xml.get("Content", {}).get("Item", {})
    if (num := xml.get("Source", {}).get("Number_Files")) is None or num == 1 or num == "1":
        details.infringing_file = item_or_items.get("Title") if isinstance(item_or_items, dict) else None
    elif isinstance(item_or_items, list) and len(item_or_items) > 0:
        details.infringing_file = item_or_items[0].get("Title")
    return details


def create_copyright_notice_ticket(
    mail: str, ip: str | None, time_str: str | None, dns: str, file_name: str | None
) -> str:
    with (BASE_DIR / "resources" / "copyright_notice_preamble.html").open() as file:
        preamble = file.read()
    with (BASE_DIR / "resources" / "copyright_notice_content.html").open() as file:
        content = file.read().format(usermail=mail, ip=ip, time_str=time_str, dns=dns, file=file_name)
    return preamble + content + "\n  </body>\n</html>\n"


def try_translate_ip_address_to_domain(ip: str | None) -> str | None:
    if not ip:
        return None
    try:
        data = socket.gethostbyaddr(ip)
        return data[0]
    except socket.herror:
        return None


def get_custom_field_value_from_transaction(
    transaction: structs.TransactionStruct, name: str
) -> structs.CustomFieldStruct | None:
    if transaction.ticket_obj is None or not transaction.ticket_obj.custom_fields:
        return None
    for cf in transaction.ticket_obj.custom_fields:
        if cf.name == name:
            return cf
    return None


def get_prominent_contacts() -> dict[str, list[str]]:
    with (BASE_DIR / "resources" / "prominent_contacts.json").open("r") as f:
        return msgspec.json.decode(f.read(), type=dict[str, list[str]])


def get_auto_reply_map() -> dict[str, dict[Literal["Content", "Subject"], list[str]]]:
    with (BASE_DIR / "resources" / "auto_reply_map.json").open("r") as f:
        return msgspec.json.decode(f.read(), type=dict[str, dict[Literal["Content", "Subject"], list[str]]])


def get_return_mail_map() -> list[dict[Literal["Subject", "Sender", "Content"], str | None]]:
    with (BASE_DIR / "resources" / "return_mail_map.json").open("r") as f:
        return msgspec.json.decode(f.read(), type=list[dict[Literal["Subject", "Sender", "Content"], str | None]])
