"""
Pure-Python form-state model + Study builder for the raster
Create-Study-Area dialog.

Kept Qt-free so the validation rules can be unit-tested without QGIS
or PyQt installed. The Qt dialog
(create_raster_study_area_dialog.CreateRasterStudyAreaDialog) imports
from here and pumps widget state through `build_raster_study_from_form_state`
on Accept.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import List
import uuid

from .comment_formats import apply_sidecar
from .models import Assessment, LayerKind, LayerRef, RasterOverlaySpec


# UI option lists. Declared here (not in the Qt dialog) so the same
# canonical set is visible to tests and to any future programmatic
# study-area creator (e.g. a recorder recipe).
RESAMPLING_OPTIONS = ("nearest", "bilinear", "cubic", "mode")
OUTPUT_FORMAT_OPTIONS = ("GTiff", "COG")


@dataclass
class InputCarryForward:
    """One row of the per-input carry-forward editor."""
    name: str
    bands_to_carry: List[int] = field(default_factory=list)
    rat_columns_to_carry: List[str] = field(default_factory=list)
    # Phase 3.5b: canonical role vocabulary ("target" | "overlay").
    # Legacy "included" still accepted by LayerRef.from_dict for the
    # grace cycle but new form-state writes use "overlay".
    role: str = "overlay"


@dataclass
class RasterStudyFormState:
    """Snapshot of dialog form values, in a Qt-free shape."""
    name: str
    description: str = ""
    # Q6b — rich body for the emds_comments_html metadata sidecar; the
    # canonical comments column keeps the plain `description`.
    description_html: str = ""
    inputs: List[InputCarryForward] = field(default_factory=list)
    snap_to: str = ""
    resampling: str = "nearest"
    output_format: str = "GTiff"
    output_path: str = ""


class RasterStudyFormError(ValueError):
    """Raised by build_raster_study_from_form_state on invalid input."""


def build_raster_study_from_form_state(state: RasterStudyFormState) -> Assessment:
    """Build a Study object from the dialog's form state.

    Mirrors the validation rules RasterOverlay.run enforces, but raises
    a UI-friendly error here so the dialog can surface the first
    failure as a message box instead of letting the overlay primitive
    blow up later.
    """
    if not state.name.strip():
        raise RasterStudyFormError("Study area name is required.")
    # Phase 3.5e: single-raster study areas are valid (degenerate
    # passthrough — RasterOverlay handles it). Zero is still invalid.
    if len(state.inputs) < 1:
        raise RasterStudyFormError(
            "A raster study area needs at least 1 input raster."
        )
    input_names = [inp.name for inp in state.inputs]
    if len(input_names) != len(set(input_names)):
        raise RasterStudyFormError(
            "Each input raster must be listed once. Got duplicates: "
            f"{[n for n in input_names if input_names.count(n) > 1]}"
        )
    if state.snap_to and state.snap_to not in input_names:
        raise RasterStudyFormError(
            f"snap_to layer '{state.snap_to}' is not in the input list."
        )
    if not state.output_path.strip():
        raise RasterStudyFormError(
            "Output overlay raster path is required."
        )
    for inp in state.inputs:
        if not inp.bands_to_carry and not inp.rat_columns_to_carry:
            raise RasterStudyFormError(
                f"Input raster '{inp.name}' must contribute at least "
                "one band or RAT column to the output."
            )

    refs = [
        LayerRef(
            name=inp.name,
            kind=LayerKind.RASTER,
            role=inp.role,
            bands_to_carry=list(inp.bands_to_carry),
            rat_columns_to_carry=list(inp.rat_columns_to_carry),
        )
        for inp in state.inputs
    ]
    overlay_spec = RasterOverlaySpec(
        snap_to=state.snap_to or state.inputs[0].name,
        resampling=state.resampling or "nearest",
        output_format=state.output_format or "GTiff",
        output_path=state.output_path,
    )
    study = Assessment(
        id=str(uuid.uuid4()),
        name=state.name.strip(),
        description=state.description.strip(),
        input_layers=input_names,
        kind=LayerKind.RASTER,
        input_layer_refs=refs,
        raster_overlay=overlay_spec,
    )
    study.metadata = apply_sidecar(study.metadata, state.description_html)
    return study
