from __future__ import annotations

import re

from app.core.agent.processes.v2.workflows.doc_update_from_feature_v2.workflow_runtime.models import (
    AnalyticsMeta,
    RequirementUnit,
)


class MarkdownHeadingScanner:
    _HEADING_RE = re.compile(r"^(#{1,6})\s+(.+?)\s*$")
    _ROOT_RE = re.compile(r"^(?:6(?:[.)\s]|$)|функциональные требования\b|описание изменений\b)", re.IGNORECASE)
    _SECTION_RE = re.compile(r"^6(?:\.\d+)+(?:\s|$|[.)])")

    def find_root(self, lines: list[str]) -> tuple[int, int, int] | None:
        headings = list(self.iter_headings(lines))
        for idx, (line_index, level, title) in enumerate(headings):
            if not self._ROOT_RE.match(title.strip()):
                continue
            end = len(lines)
            for next_line, next_level, _ in headings[idx + 1 :]:
                if next_level <= level:
                    end = next_line
                    break
            return line_index, level, end
        return None

    def split_sections(self, lines: list[str], *, section_level: int) -> tuple[list[str], list[tuple[str, list[str]]]]:
        meta_lines: list[str] = []
        sections: list[tuple[str, list[str]]] = []
        current_title = ""
        current_body: list[str] = []
        seen_section = False

        for raw in lines:
            heading = self._match_heading(raw)
            if heading and heading[0] == section_level and self._SECTION_RE.match(heading[1].strip()):
                if current_title:
                    sections.append((current_title, current_body))
                current_title = heading[1].strip()
                current_body = []
                seen_section = True
                continue
            if current_title:
                current_body.append(raw)
            elif not seen_section:
                meta_lines.append(raw)

        if current_title:
            sections.append((current_title, current_body))
        return meta_lines, sections

    def iter_headings(self, lines: list[str]) -> list[tuple[int, int, str]]:
        items: list[tuple[int, int, str]] = []
        for index, raw in enumerate(lines):
            heading = self._match_heading(raw)
            if heading:
                items.append((index, heading[0], heading[1]))
        return items

    def _match_heading(self, raw: str) -> tuple[int, str] | None:
        match = self._HEADING_RE.match(raw.strip())
        if not match:
            return None
        return len(match.group(1)), match.group(2).strip()


class MarkdownMetadataParser:
    _KEY_VALUE_RE = re.compile(r"^[-*]?\s*`?([A-Za-z_][A-Za-z0-9_-]*)`?\s*:\s*(.*)$")

    def parse_block(self, lines: list[str]) -> dict[str, str]:
        values: dict[str, str] = {}
        for raw in lines:
            line = raw.strip()
            if not line:
                continue
            match = self._KEY_VALUE_RE.match(line)
            if not match:
                break
            key = match.group(1).strip().lower()
            value = self._normalize_value(match.group(2))
            values[key] = value
        return values

    def split_metadata_and_body(self, lines: list[str]) -> tuple[dict[str, str], list[str]]:
        metadata: dict[str, str] = {}
        body_index = 0
        for idx, raw in enumerate(lines):
            line = raw.strip()
            if not line:
                body_index = idx + 1
                continue
            match = self._KEY_VALUE_RE.match(line)
            if not match:
                break
            key = match.group(1).strip().lower()
            value = self._normalize_value(match.group(2))
            metadata[key] = value
            body_index = idx + 1
        return metadata, lines[body_index:]

    def _normalize_value(self, value: str) -> str:
        return value.strip().strip("`").strip('"').strip("'").strip()


class FunctionalRequirementsParser:
    _SECTION_KEY_RE = re.compile(r"^(6(?:\.\d+)+)[.)\s-]*(.*)$")

    def __init__(
        self,
        scanner: MarkdownHeadingScanner | None = None,
        metadata_parser: MarkdownMetadataParser | None = None,
    ) -> None:
        self._scanner = scanner or MarkdownHeadingScanner()
        self._metadata_parser = metadata_parser or MarkdownMetadataParser()

    def parse(self, content: str) -> tuple[AnalyticsMeta, list[RequirementUnit]]:
        lines = content.splitlines()
        meta = self._parse_preamble_meta(lines)
        root = self._scanner.find_root(lines)
        if root is None:
            return meta, []

        start, level, end = root
        root_meta_lines, sections = self._scanner.split_sections(lines[start + 1 : end], section_level=level + 1)
        root_meta = self._metadata_parser.parse_block(root_meta_lines)
        meta.domain = root_meta.get("domain", meta.domain)
        meta.subdomain = root_meta.get("sub_domain", root_meta.get("subdomain", meta.subdomain))
        meta.application = root_meta.get("application", meta.application)
        meta.platform = root_meta.get("platform", meta.platform)

        units: list[RequirementUnit] = []
        for raw_heading, body_lines in sections:
            metadata, body = self._metadata_parser.split_metadata_and_body(body_lines)
            section_key, heading = self._split_heading(raw_heading)
            units.append(
                RequirementUnit(
                    section_key=section_key,
                    heading=heading,
                    body="\n".join(body).strip(),
                    metadata=metadata,
                )
            )
        return meta, units

    def _parse_preamble_meta(self, lines: list[str]) -> AnalyticsMeta:
        preamble: list[str] = []
        for raw in lines:
            if raw.strip().startswith("#"):
                break
            preamble.append(raw)
        values = self._metadata_parser.parse_block(preamble)
        return AnalyticsMeta(
            analysis_id=values.get("analysis_id", ""),
            application=values.get("application", ""),
            platform=values.get("platform", ""),
            domain=values.get("domain", ""),
            subdomain=values.get("sub_domain", values.get("subdomain", "")),
        )

    def _split_heading(self, raw_heading: str) -> tuple[str, str]:
        match = self._SECTION_KEY_RE.match(raw_heading.strip())
        if not match:
            return "", raw_heading.strip()
        section_key = match.group(1).strip()
        heading = match.group(2).strip() or section_key
        return section_key, heading
