from __future__ import annotations

from typing import Any, Dict, List, Optional

from export.models import (
    IDExportCharacterStyle,
    IDExportDocument,
    IDExportImageFrame,
    IDExportPage,
    IDExportPageNumber,
    IDExportParagraph,
    IDExportParagraphStyle,
    IDExportRun,
    IDExportShape,
    IDExportTable,
    IDExportTableCell,
    IDExportTableRow,
    IDExportTextFrame,
)


class IDExportDocumentFactory:
    """Builds IDExportDocument from a validated idconvert_export.json dict."""

    @staticmethod
    def from_dict(data: dict) -> IDExportDocument:
        doc_info = data.get('document', {})
        styles_data = data.get('styles', {})

        paragraph_styles = [
            IDExportParagraphStyleFactory.from_dict(s)
            for s in styles_data.get('paragraph', [])
        ]
        character_styles = [
            IDExportCharacterStyleFactory.from_dict(s)
            for s in styles_data.get('character', [])
        ]

        pages = [
            IDExportPageFactory.from_dict(p)
            for p in data.get('pages', [])
        ]

        # Find first page_number item across all pages for footer use
        page_number_style: Optional[IDExportPageNumber] = None
        for page in pages:
            for item in page.items:
                if isinstance(item, IDExportPageNumber):
                    page_number_style = item
                    break
            if page_number_style:
                break

        return IDExportDocument(
            name=doc_info.get('name', 'Document'),
            page_width_pt=float(doc_info.get('page_width_pt', 595.28)),
            page_height_pt=float(doc_info.get('page_height_pt', 841.89)),
            page_count=int(doc_info.get('page_count', len(pages))),
            facing_pages=bool(doc_info.get('facing_pages', False)),
            paragraph_styles=paragraph_styles,
            character_styles=character_styles,
            colors=data.get('colors', {}),
            fonts_used=data.get('fonts_used', []),
            pages=pages,
            page_number_style=page_number_style,
        )


class IDExportParagraphStyleFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportParagraphStyle:
        return IDExportParagraphStyle(
            name=data.get('name', ''),
            font=data.get('font'),
            size_pt=_optional_float(data.get('size_pt')),
            leading_pt=_optional_float(data.get('leading_pt')),
            space_before_pt=float(data.get('space_before_pt', 0)),
            space_after_pt=float(data.get('space_after_pt', 0)),
            alignment=data.get('alignment', 'left'),
            color_hex=data.get('color_hex'),
            bold=bool(data.get('bold', False)),
            italic=bool(data.get('italic', False)),
        )


class IDExportCharacterStyleFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportCharacterStyle:
        return IDExportCharacterStyle(
            name=data.get('name', ''),
            font=data.get('font'),
            bold=bool(data.get('bold', False)),
            italic=bool(data.get('italic', False)),
            color_hex=data.get('color_hex'),
        )


class IDExportPageFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportPage:
        items = [
            IDExportItemFactory.from_dict(item)
            for item in data.get('items', [])
            if item
        ]
        items = [i for i in items if i is not None]

        return IDExportPage(
            page_number=data.get('page_number', 1),
            width_pt=float(data.get('width_pt', 595.28)),
            height_pt=float(data.get('height_pt', 841.89)),
            items=items,
        )


class IDExportItemFactory:
    @staticmethod
    def from_dict(data: dict):
        item_type = data.get('type', '')
        if item_type == 'text_frame':
            return IDExportTextFrameFactory.from_dict(data)
        if item_type == 'image_frame':
            return IDExportImageFrameFactory.from_dict(data)
        if item_type == 'table':
            return IDExportTableFactory.from_dict(data)
        if item_type == 'page_number':
            return IDExportPageNumberFactory.from_dict(data)
        if item_type == 'shape':
            return IDExportShapeFactory.from_dict(data)
        # frame_group, column_break, and unknown types not converted
        return None


class IDExportShapeFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportShape:
        return IDExportShape(
            id=str(data.get('id', '')),
            x_pt=float(data.get('x_pt', 0)),
            y_pt=float(data.get('y_pt', 0)),
            width_pt=float(data.get('width_pt', 0)),
            height_pt=float(data.get('height_pt', 0)),
            shape_type=data.get('shape_type', 'rect'),
            fill_hex=data.get('fill_hex'),
            stroke_hex=data.get('stroke_hex'),
            stroke_weight_pt=float(data.get('stroke_weight_pt', 0)),
        )


class IDExportTextFrameFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportTextFrame:
        paragraphs = [
            IDExportParagraphFactory.from_dict(p)
            for p in data.get('paragraphs', [])
        ]
        return IDExportTextFrame(
            id=str(data.get('id', '')),
            thread_id=str(data.get('thread_id', '')),
            thread_position=int(data.get('thread_position', 1)),
            x_pt=float(data.get('x_pt', 0)),
            y_pt=float(data.get('y_pt', 0)),
            width_pt=float(data.get('width_pt', 0)),
            height_pt=float(data.get('height_pt', 0)),
            column_count=int(data.get('column_count', 1)),
            paragraphs=paragraphs,
        )


class IDExportParagraphFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportParagraph:
        runs = [
            IDExportRunFactory.from_dict(r)
            for r in data.get('runs', [])
        ]
        return IDExportParagraph(
            style=data.get('style', ''),
            text=data.get('text', ''),
            runs=runs,
        )


class IDExportRunFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportRun:
        return IDExportRun(
            text=data.get('text', ''),
            style=data.get('style'),
            bold=bool(data.get('bold', False)),
            italic=bool(data.get('italic', False)),
            color_hex=data.get('color_hex'),
            font=data.get('font'),
            size_pt=_optional_float(data.get('size_pt')),
        )


class IDExportImageFrameFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportImageFrame:
        return IDExportImageFrame(
            id=str(data.get('id', '')),
            x_pt=float(data.get('x_pt', 0)),
            y_pt=float(data.get('y_pt', 0)),
            width_pt=float(data.get('width_pt', 0)),
            height_pt=float(data.get('height_pt', 0)),
            image_name=data.get('image_name', 'image'),
            image_data_b64=data.get('image_data_b64'),
            fit_mode=data.get('fit_mode', 'fill_proportionally'),
        )


class IDExportTableFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportTable:
        rows = [
            IDExportTableRowFactory.from_dict(r)
            for r in data.get('rows', [])
        ]
        return IDExportTable(
            id=str(data.get('id', '')),
            x_pt=float(data.get('x_pt', 0)),
            y_pt=float(data.get('y_pt', 0)),
            width_pt=float(data.get('width_pt', 0)),
            column_widths_pt=[float(w) for w in data.get('column_widths_pt', [])],
            rows=rows,
        )


class IDExportTableRowFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportTableRow:
        cells = [
            IDExportTableCellFactory.from_dict(c)
            for c in data.get('cells', [])
        ]
        return IDExportTableRow(
            is_header=bool(data.get('is_header', False)),
            cells=cells,
        )


class IDExportTableCellFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportTableCell:
        return IDExportTableCell(
            text=data.get('text', ''),
            style=data.get('style'),
            col_span=int(data.get('col_span', 1)),
            row_span=int(data.get('row_span', 1)),
            background_hex=data.get('background_hex'),
            text_color_hex=data.get('text_color_hex'),
        )


class IDExportPageNumberFactory:
    @staticmethod
    def from_dict(data: dict) -> IDExportPageNumber:
        return IDExportPageNumber(
            id=str(data.get('id', '')),
            x_pt=float(data.get('x_pt', 0)),
            y_pt=float(data.get('y_pt', 0)),
            font=data.get('font'),
            size_pt=float(data.get('size_pt', 9)),
            color_hex=data.get('color_hex', '000000') or '000000',
            alignment=data.get('alignment', 'center'),
        )


def _optional_float(value) -> Optional[float]:
    if value is None:
        return None
    try:
        return float(value)
    except (TypeError, ValueError):
        return None
