from pathlib import Path
from datetime import datetime
from typing import Any, Dict
from dataclasses import is_dataclass, asdict

from jinja2 import Environment, FileSystemLoader


class PDFService:
    def __init__(self):
        self.template_dir = Path(__file__).parent.parent.parent / "infrastructure" / "pdf" / "templates"
        self.env = Environment(
            loader=FileSystemLoader(str(self.template_dir)),
            autoescape=True
        )
        self.env.filters['format_date'] = self._format_date
        self.env.filters['format_date_short'] = self._format_date_short
        self.env.filters['sort_by_date'] = self._sort_by_date
        self.template = self.env.get_template("ficha_consultor.html")

    def _format_date(self, date_str: str) -> str:
        if not date_str:
            return "-"
        try:
            if "/" in date_str:
                return date_str
            dt = datetime.fromisoformat(date_str.replace("Z", "+00:00"))
            return dt.strftime("%d/%m/%Y")
        except (ValueError, AttributeError):
            return str(date_str)

    def _format_date_short(self, date_str: str) -> str:
        if not date_str:
            return "-"
        try:
            meses = ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun',
                     'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']
            if "/" in date_str:
                parts = date_str.split("/")
                if len(parts) >= 2:
                    mes = int(parts[1]) if len(parts) == 3 else int(parts[0])
                    ano = parts[2] if len(parts) == 3 else parts[1]
                    return f"{meses[mes-1]}/{ano}"
                return date_str
            dt = datetime.fromisoformat(date_str.replace("Z", "+00:00"))
            return f"{meses[dt.month-1]}/{dt.year}"
        except (ValueError, AttributeError, IndexError):
            return str(date_str)[:10] if date_str else "-"

    def _sort_by_date(self, items, *fields):
        if not items or not fields:
            return items

        def extract_field(value, field):
            for part in field.split("."):
                if value is None:
                    return None
                if isinstance(value, DictWrapper):
                    value = value.get(part)
                elif isinstance(value, dict):
                    value = value.get(part)
                else:
                    value = getattr(value, part, None)
            return value

        def parse_date(value):
            if value is None:
                return None
            if isinstance(value, datetime):
                return value
            if isinstance(value, (int, float)):
                year = int(value)
                return datetime(year, 1, 1)
            if not isinstance(value, str):
                return None
            text = value.strip()
            if not text:
                return None
            if text.isdigit() and len(text) == 4:
                return datetime(int(text), 1, 1)
            if "/" in text:
                parts = text.split("/")
                try:
                    if len(parts) == 3:
                        day = int(parts[0])
                        month = int(parts[1])
                        year = int(parts[2])
                        return datetime(year, month, day)
                    if len(parts) == 2:
                        month = int(parts[0])
                        year = int(parts[1])
                        return datetime(year, month, 1)
                except ValueError:
                    return None
            try:
                return datetime.fromisoformat(text.replace("Z", "+00:00"))
            except ValueError:
                return None

        def sort_key(item):
            value = None
            for field in fields:
                value = parse_date(extract_field(item, field))
                if value is not None:
                    break
            if value is None:
                return (0, datetime.min)
            return (1, value)

        return sorted(items, key=sort_key, reverse=True)

    def _consultor_to_dict(self, consultor: Any) -> Dict:
        if is_dataclass(consultor) and not isinstance(consultor, type):
            return asdict(consultor)
        elif hasattr(consultor, 'model_dump'):
            return consultor.model_dump()
        elif hasattr(consultor, 'dict'):
            return consultor.dict()
        elif isinstance(consultor, dict):
            return consultor
        else:
            return vars(consultor)

    def gerar_ficha_consultor(self, consultor: Any, raw_document: Any = None) -> bytes:
        from weasyprint import HTML, CSS

        consultor_data = self._consultor_to_dict(consultor)
        raw_data = self._consultor_to_dict(raw_document) if raw_document else {}

        html_content = self.template.render(
            consultor=ConsultorWrapper(consultor_data),
            raw=DictWrapper(raw_data) if raw_data else DictWrapper({}),
            data_geracao=datetime.now().strftime("%d/%m/%Y às %H:%M"),
            pontuacao_coord=self._extrair_pontuacao_coord(consultor_data),
        )

        css_path = self.template_dir / "styles.css"

        pdf_bytes = HTML(
            string=html_content,
            base_url=str(self.template_dir)
        ).write_pdf(
            stylesheets=[CSS(filename=str(css_path))]
        )

        return pdf_bytes

    def _extrair_pontuacao_coord(self, consultor_data: Dict) -> Dict:
        result = {}
        pontuacao = consultor_data.get('pontuacao')
        if not pontuacao:
            return result

        bloco_a = pontuacao.get('bloco_a', {})
        atuacoes = bloco_a.get('atuacoes', [])

        for atuacao in atuacoes:
            codigo = atuacao.get('codigo', '')
            result[codigo] = {
                'base': atuacao.get('base', 0),
                'tempo': atuacao.get('tempo', 0),
                'bonus': atuacao.get('bonus', 0),
                'total': atuacao.get('total', 0),
            }

        return result

    def gerar_pdf_equipe(self, tema: str, area_avaliacao: str, consultores: list) -> bytes:
        from weasyprint import HTML, CSS

        template = self.env.get_template("equipe_consultores.html")

        ies_set = set()
        areas_set = set()
        for c in consultores:
            if c.get('ies'):
                ies_set.add(c['ies'])
            for area in c.get('areas_avaliacao', []):
                areas_set.add(area)

        estatisticas = {
            'total': len(consultores),
            'ativos': sum(1 for c in consultores if c.get('situacao', '').lower().find('atividade') >= 0),
            'coordenadores': sum(1 for c in consultores if c.get('foi_coordenador')),
            'premiados': sum(1 for c in consultores if c.get('foi_premiado')),
            'ies_distintas': len(ies_set),
            'areas_cobertas': len(areas_set),
            'lista_ies': sorted(list(ies_set)),
            'lista_areas': sorted(list(areas_set)),
        }

        html_content = template.render(
            tema=tema,
            area_avaliacao=area_avaliacao,
            consultores=consultores,
            estatisticas=estatisticas,
            data_geracao=datetime.now().strftime("%d/%m/%Y às %H:%M"),
        )

        css_path = self.template_dir / "styles.css"

        pdf_bytes = HTML(
            string=html_content,
            base_url=str(self.template_dir)
        ).write_pdf(
            stylesheets=[CSS(filename=str(css_path))]
        )

        return pdf_bytes


class ConsultorWrapper:
    def __init__(self, data: Dict):
        self._data = data

    def __getattr__(self, name: str):
        value = self._data.get(name)
        if value is None:
            return None
        if isinstance(value, dict):
            return DictWrapper(value)
        if isinstance(value, list):
            return [DictWrapper(item) if isinstance(item, dict) else item for item in value]
        return value

    def __bool__(self):
        return bool(self._data)


class DictWrapper:
    def __init__(self, data: Dict):
        self._data = data

    def __getattr__(self, name: str):
        value = self._data.get(name)
        if value is None:
            return None
        if isinstance(value, dict):
            return DictWrapper(value)
        if isinstance(value, list):
            return [DictWrapper(item) if isinstance(item, dict) else item for item in value]
        return value

    def __bool__(self):
        return bool(self._data)

    def __str__(self):
        return str(self._data)

    def get(self, key: str, default=None):
        return self._data.get(key, default)
