"""
Absfuyu: Google form
--------------------
Google form

Version: 6.7.0
Date updated: 26/04/2026 (dd/mm/yyyy)
"""

# Module level
# ---------------------------------------------------------------------------
__all__ = ["GoogleFormParser"]

# Library
# ---------------------------------------------------------------------------
import json
import re
from typing import Self

import requests

from absfuyu.extra.gform.models import FormID, GoogleForm, Section
from absfuyu.extra.gform.question_models import (
    Answer,
    Option,
    Question,
    QuestionType,
    Validation,
    ValidationSubtype,
    ValidationType,
)


# Class
# ---------------------------------------------------------------------------
class GoogleFormParser:
    def __init__(self, form_url: str, *, engine: type[GoogleForm] | None = None) -> None:
        """
        Parse question from google form and create Google Form object

        Parameters
        ----------
        form_url : str
            Form link

        engine : type[GoogleForm] | None, optional
            Google Form engine (advanced use), by default None
        """
        self.form_url = form_url
        self.engine = engine if engine is not None else GoogleForm

    @classmethod
    def from_form_id(cls, form_id: FormID) -> Self:
        return cls(form_id.view)

    def _load_raw_data(self) -> list[list | str | int | None]:
        headers = {"User-Agent": "Mozilla/5.0"}
        html = requests.get(self.form_url, headers=headers).text

        match = re.search(r"FB_PUBLIC_LOAD_DATA_ = (.*?);</script>", html)
        if not match:
            raise ValueError("Form structure not found")

        return json.loads(match.group(1))

    def _parse_options(self, ans: list) -> tuple[list[Option], dict[str, int | None]]:
        options = []
        branching = {}

        if len(ans) > 1 and isinstance(ans[1], list):
            for opt in ans[1]:
                if isinstance(opt, list) and opt:
                    text = opt[0]

                    # Branching detection
                    jump_target = None
                    if len(opt) > 4 and opt[2] not in (None, 0):
                        jump_target = opt[2]

                    options.append(
                        Option(
                            text=text,
                            raw=opt,
                        )
                    )

                    branching[text] = jump_target

        return options, branching

    def _parse_validation(self, ans: list) -> list[Validation]:
        result = []

        # validation usually at index 4
        if len(ans) > 4 and isinstance(ans[4], list):
            for rule in ans[4]:
                if isinstance(rule, list) and len(rule) >= 4:
                    result.append(
                        # Validation(
                        #     type=rule[0],
                        #     subtype=rule[1],
                        #     args=rule[2],
                        #     message=rule[3],
                        # )
                        Validation(
                            type=ValidationType.from_code(int(rule[0])),
                            subtype=ValidationSubtype.from_code(int(rule[1])),
                            args=rule[2],
                            message=rule[3],
                        )
                    )

        return result

    def _parse_answer(self, ans: list) -> tuple[Answer, dict[str, int | None]]:
        entry_id = f"entry.{ans[0]}"
        required = bool(ans[2]) if len(ans) > 2 else False

        options, branching = self._parse_options(ans)
        validation = self._parse_validation(ans)

        answer = Answer(
            entry_id=entry_id,
            required=required,
            options=options,
            validation=validation,
            raw=ans,
        )

        return answer, branching

    def _parse_question(self, q: list, page_index: int) -> Question | None:
        qtype = QuestionType.from_code(q[3])

        # SECTION = page break -> skip but update outside
        if qtype == QuestionType.SECTION:
            return None

        question_id = q[0]
        text = q[1]
        description = q[2]
        answer_block = q[4] or []

        answers: list[Answer] = []
        branching: dict[str, int | None] = {}

        for ans in answer_block:
            parsed_answer, br = self._parse_answer(ans)
            answers.append(parsed_answer)

            # merge branching
            branching.update(br)

        required = any(a.required for a in answers)
        entry_ids = [a.entry_id for a in answers]

        return Question(
            question_id=question_id,
            text=text,
            description=description,
            type=qtype,
            answers=answers,
            required=required,
            entry_ids=entry_ids,
            branching=branching,
            page_index=page_index,
            raw=q,
        )

    def _parse_section(self, q: list, page_index: int = 0) -> Section:
        section_id = q[0]
        text = q[1]
        description = q[2]

        return Section(
            section_id=section_id,
            text=text,
            description=description,
            page_index=page_index,
            raw=q,
        )

    def parse(self):
        raw_data = self._load_raw_data()
        question_blocks = raw_data[1][1]

        questions: list[Question] = []
        sections: list[Section] = []

        page_index = 0
        page_count = 1

        for q in question_blocks:
            try:
                qtype = QuestionType.from_code(q[3])

                # SECTION -> new page
                if qtype == QuestionType.SECTION:
                    page_index += 1
                    page_count += 1
                    s = self._parse_section(q, page_index=page_index)
                    sections.append(s)
                    continue

                question = self._parse_question(q, page_index)

                if question:
                    questions.append(question)

            except Exception as error:
                print(f"Unable to parse question: {error}")  # Logger soon
                continue

        return self.engine(
            form_id=FormID.from_link(self.form_url),
            questions=questions,
            sections=sections,
            is_multipage=page_count > 1,
            page_count=page_count,
        )
