from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime

from openpyxl.worksheet.worksheet import Worksheet

from xllayout.components.chart import MatplotlibChart
from xllayout.components.kpi import KpiCard
from xllayout.components.section import SectionHeader
from xllayout.components.table import TableBlock
from xllayout.components.title import TitleBlock
from xllayout.core.canvas import SheetCanvas
from xllayout.core.grid import Grid12, GridConfig
from xllayout.core.theme import LightTheme, Theme


@dataclass(frozen=True)
class DataQualityRow:
    dimension: str
    total_base: int
    total_dimension: int
    with_data: int
    without_data: int
    coverage: float
    missing_ratio: float


@dataclass(frozen=True)
class RankedItem:
    item_id: int | None
    name: str
    total: int


@dataclass(frozen=True)
class ExecutiveSummaryData:
    scope: str
    generated_at: datetime
    total_base_records: int
    segments_in_detail: int
    primary_coverage: float
    secondary_coverage: float
    quality_rows: list[DataQualityRow]
    top_primary: list[RankedItem]
    top_secondary: list[RankedItem]


class ExecutiveSummaryLayout:
    def __init__(
        self,
        data: ExecutiveSummaryData,
        theme: Theme | None = None,
        detect_collisions: bool = False,
        grid_config: GridConfig | None = None,
    ) -> None:
        self.data = data
        self.theme = theme or LightTheme()
        self.detect_collisions = detect_collisions
        self.grid_config = grid_config or GridConfig(start_col=1, end_col=24)

    def build(self, ws: Worksheet) -> None:
        canvas = SheetCanvas(ws=ws, theme=self.theme, detect_collisions=self.detect_collisions)
        grid = Grid12(canvas=canvas, config=self.grid_config)

        subtitle = f"Generated: {self.data.generated_at.strftime('%Y-%m-%d %H:%M:%S')} | Scope: {self.data.scope}"
        title_box = grid.put(
            TitleBlock(title="Executive Summary", subtitle=subtitle, span_cols=12),
            y=1,
            x=1,
            w=12,
            h=2,
        )

        kpi_y = title_box.r2 + self.theme.spacing.section_gap
        kpis = [
            (
                "Total base records",
                self.data.total_base_records,
                self.theme.number_formats.integer,
            ),
            (
                "Segments in detail",
                self.data.segments_in_detail,
                self.theme.number_formats.integer,
            ),
            (
                "Primary coverage",
                self.data.primary_coverage,
                self.theme.number_formats.percent,
            ),
            (
                "Secondary coverage",
                self.data.secondary_coverage,
                self.theme.number_formats.percent,
            ),
        ]

        kpi_boxes = []
        for idx, (label, value, fmt) in enumerate(kpis):
            x = 1 + (idx * 3)
            box = grid.put(
                KpiCard(label=label, value=value, value_format=fmt, span_cols=3, height_rows=4),
                y=kpi_y,
                x=x,
                w=3,
                h=4,
            )
            kpi_boxes.append(box)

        next_y = max(box.r2 for box in kpi_boxes) + self.theme.spacing.section_gap

        quality_header = grid.put(
            SectionHeader(text="Data quality by dimension", span_cols=12),
            y=next_y,
            x=1,
            w=12,
            h=1,
        )

        quality_rows = [
            [
                row.dimension,
                row.total_base,
                row.total_dimension,
                row.with_data,
                row.without_data,
                row.coverage,
                row.missing_ratio,
            ]
            for row in self.data.quality_rows
        ]

        quality_table_height = max(1 + len(quality_rows), 7)
        quality_box = grid.put(
            TableBlock(
                headers=[
                    "dimension",
                    "total_base",
                    "total_dimension",
                    "with_data",
                    "without_data",
                    "coverage",
                    "missing_ratio",
                ],
                rows=quality_rows,
                table_style="zebra_compact",
                number_formats_by_col={
                    2: self.theme.number_formats.integer,
                    3: self.theme.number_formats.integer,
                    4: self.theme.number_formats.integer,
                    5: self.theme.number_formats.integer,
                    6: self.theme.number_formats.percent,
                    7: self.theme.number_formats.percent,
                },
                col_widths=[16, 14, 14, 12, 10, 11, 11],
            ),
            y=quality_header.r2 + 1,
            x=1,
            w=12,
            h=quality_table_height,
        )

        tops_header_y = quality_box.r2 + self.theme.spacing.section_gap
        tops_header = grid.put(
            SectionHeader(text="Top 10 breakdown", span_cols=12),
            y=tops_header_y,
            x=1,
            w=12,
            h=1,
        )

        top_tables_y = tops_header.r2 + 1
        top_primary_rows = [[item.item_id, item.name, item.total] for item in self.data.top_primary]
        top_secondary_rows = [[item.item_id, item.name, item.total] for item in self.data.top_secondary]
        top_table_height = max(1 + max(len(top_primary_rows), len(top_secondary_rows)), 7)

        top_primary_box = grid.put(
            TableBlock(
                headers=["primary_id", "primary_name", "total"],
                rows=top_primary_rows,
                table_style="simple",
                number_formats_by_col={3: self.theme.number_formats.integer},
                col_widths=[12, 30, 16],
            ),
            y=top_tables_y,
            x=1,
            w=6,
            h=top_table_height,
        )

        top_secondary_box = grid.put(
            TableBlock(
                headers=["secondary_id", "secondary_name", "total"],
                rows=top_secondary_rows,
                table_style="simple",
                number_formats_by_col={3: self.theme.number_formats.integer},
                col_widths=[12, 30, 16],
            ),
            y=top_tables_y,
            x=7,
            w=6,
            h=top_table_height,
        )

        charts_y = max(top_primary_box.r2, top_secondary_box.r2) + self.theme.spacing.section_gap
        if top_primary_rows:
            grid.put(
                MatplotlibChart(
                    chart_type="bar",
                    categories=[item.name for item in self.data.top_primary],
                    values=[item.total for item in self.data.top_primary],
                    title="Top primary",
                    span_cols=6,
                    span_rows=12,
                ),
                y=charts_y,
                x=1,
                w=6,
                h=12,
            )

        if top_secondary_rows:
            grid.put(
                MatplotlibChart(
                    chart_type="bar",
                    categories=[item.name for item in self.data.top_secondary],
                    values=[item.total for item in self.data.top_secondary],
                    title="Top secondary",
                    span_cols=6,
                    span_rows=12,
                ),
                y=charts_y,
                x=7,
                w=6,
                h=12,
            )
