import json
from collections.abc import Iterator, Sequence
from dataclasses import dataclass
from functools import cached_property
from pathlib import Path

from my.core import Res, datetime_aware, get_files
from my.core.compat import fromisoformat
from my.experimental.destructive_parsing import Manager

from my.config import topcoder as config  # type: ignore[attr-defined]  # isort: skip


def inputs() -> Sequence[Path]:
    return get_files(config.export_path)


@dataclass
class Competition:
    contest_id: str
    contest: str
    percentile: float
    date_str: str

    @cached_property
    def uid(self) -> str:
        return self.contest_id

    @cached_property
    def when(self) -> datetime_aware:
        return fromisoformat(self.date_str)

    @classmethod
    def make(cls, j) -> Iterator[Res['Competition']]:
        assert isinstance(j.pop('rating'), float)
        assert isinstance(j.pop('placement'), int)

        cid = j.pop('challengeId')
        cname = j.pop('challengeName')
        percentile = j.pop('percentile')
        date_str = j.pop('date')

        yield cls(
            contest_id=cid,
            contest=cname,
            percentile=percentile,
            date_str=date_str,
        )


def _parse_one(p: Path) -> Iterator[Res[Competition]]:
    d = json.loads(p.read_text())

    # TODO manager should be a context manager?
    m = Manager()

    h = m.helper(d)
    h.pop_if_primitive('version', 'id')

    h = h.zoom('result')
    h.check('success', expected=True)
    h.check('status', 200)
    h.pop_if_primitive('metadata')

    h = h.zoom('content')
    h.pop_if_primitive('handle', 'handleLower', 'userId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy')

    # NOTE at the moment it's empty for me, but it will result in an error later if there is some data here
    h.zoom('DEVELOP').zoom('subTracks')

    h = h.zoom('DATA_SCIENCE')
    # TODO multi zoom? not sure which axis, e.g.
    # zoom('SRM', 'history') or zoom('SRM', 'MARATHON_MATCH')
    # or zoom(('SRM', 'history'), ('MARATHON_MATCH', 'history'))
    srms = h.zoom('SRM').zoom('history')
    mms = h.zoom('MARATHON_MATCH').zoom('history')

    for c in srms.item + mms.item:
        # NOTE: so here we are actually just using pure dicts in .make method
        # this is kinda ok since it will be checked by parent Helper
        # but also expects cooperation from .make method (e.g. popping items from the dict)
        # could also wrap in helper and pass to .make .. not sure
        # an argument could be made that .make isn't really a class methond..
        # it's pretty specific to this parser onl
        yield from Competition.make(j=c)

    yield from m.check()


def data() -> Iterator[Res[Competition]]:
    *_, last = inputs()
    return _parse_one(last)
