# Copyright 2025-2026 Dorsal Hub LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import re

# This script builds the BaseModel attributes for a pydantic validator from mediainfo.xsd
# It is provided for reference.
# - source: https://mediaarea.net/mediainfo/mediainfo.xsd
# - asof: 2025-12-11
# - version: 18.08
# - sha256: 89ee6a3b60d78b6b810cb9946d174d0596986be53ef0f205a3df9bba46393b4a
#
# Assumptions:
# - The goal is to get the mediainfo data for a given file
# - This mediainfo data is being generated by pymediainfo and via dorsal
# - The user probably has no control over the mediainfo data in the file
# - The user has limited to no control over the way mediainfo data parsing is done here
#    - Raising blocking errors is unhelpful
#       - if a user wants to troubleshoot, they can run mediainfo utility separately
#    - Better to use a 'best effort' approach, and return None for mis-shapen/garbled fields
# - The types declared in the XSD will almost always be observed
# - When data comes in that cannot be cast as the correct type:
#   - Try our best (e.g. int='3/12' -> 3)
#   - If we don't have a rule, we return `None`

_RX_ELEMENT = re.compile(
    r"^\<xsd\:element\sname\=\"(?P<name>.*?)\"\s(?:type\=\"(?P<type>.*?)\"\s)?minOccurs\=\"(?P<count>.*?)\"\smaxOccurs\=\"(?P<count2>.*?)\"(?:\stype\=\"(?P<type2>.*?)\")?\s?\/?\>$"
)

_TARGET_TYPES = {
    "xsd:float": "MessyFloat",
    "xsd:integer": "MessyInteger",
    "xsd:string": "TString4096",
}


class GenerateMediaInfoTrackAttributeList:
    def __init__(self, mediainfo_xsd_path: str):
        with open(mediainfo_xsd_path) as fp:
            xml_str = fp.read()
            xml_rows = [" ".join(row.split()).strip() for row in xml_str.split("\n")]

        results: list[dict[str, str]] = []
        self.rows: list[dict[str, str]] = []

        for row in xml_rows:
            match = _RX_ELEMENT.search(row)
            if match:
                m = match.groupdict()
                assert m["name"], "Unexpected XML formatting. Check `rx_element` regex."
                assert m["type"] or m["type2"], "Unexpected XML formatting. Check `rx_element` regex."
                assert m["count"], "Unexpected XML formatting. Check `rx_element` regex."
                assert m["count2"], "Unexpected XML formatting. Check `rx_element` regex."
                results.append(m)

        for result_dict in results:
            if result_dict.get("type"):
                type_ = result_dict["type"]
            elif result_dict.get("type2"):
                type_ = result_dict["type2"]
            else:
                raise Exception("Unexpected XML formatting. Check `rx_element` regex.")

            minoccurs = result_dict["count"]
            maxoccurs = result_dict["count2"]
            name = result_dict["name"]
            record = {"Field": name, "Type": type_, "Min": minoccurs, "Max": maxoccurs}
            self.rows.append(record)

    def _parse_attributes(self):
        attributes = []
        for row in self.rows:
            if row["Type"] in _TARGET_TYPES:
                if row["Max"] == "1":
                    if "-" in row["Field"]:
                        suffix = f" = Field(default=None, alias='{row['Field']}')"
                        field = row["Field"].replace("-", "_")

                    else:
                        suffix = " = None"
                        field = row["Field"]

                else:
                    raise
                attributes.append(f"{field}: {_TARGET_TYPES[row['Type']]}{suffix}")
        attributes.append("extra: MediaInfoTrackExtra | None = None")
        return attributes

    def generate(self):
        return self._parse_attributes()
