"""
Page Setup Dialog for pyPhotoAlbum

Encapsulates all UI logic for page setup configuration,
separating presentation from business logic.
"""

import math
from typing import Optional, Dict, Any
from PyQt6.QtWidgets import (
    QDialog,
    QVBoxLayout,
    QHBoxLayout,
    QLabel,
    QDoubleSpinBox,
    QSpinBox,
    QPushButton,
    QGroupBox,
    QComboBox,
    QCheckBox,
    QRadioButton,
    QButtonGroup,
)
from pyPhotoAlbum.project import Project


class PageSetupDialog(QDialog):
    """
    Dialog for configuring page settings.

    This dialog handles all UI presentation logic for page setup,
    including page size, DPI settings, and cover configuration.
    """

    def __init__(self, parent, project: Project, initial_page_index: int = 0):
        """
        Initialize the page setup dialog.

        Args:
            parent: Parent widget
            project: Project instance containing pages and settings
            initial_page_index: Index of page to initially select
        """
        super().__init__(parent)
        self.project = project
        self.initial_page_index = initial_page_index

        self._setup_ui()
        self._connect_signals()
        self._initialize_values()

    def _setup_ui(self):
        """Create and layout all UI components."""
        self.setWindowTitle("Page Setup")
        self.setMinimumWidth(450)

        layout = QVBoxLayout()

        # Page selection group
        self._page_select_group = self._create_page_selection_group()
        layout.addWidget(self._page_select_group)

        # Cover settings group
        self._cover_group = self._create_cover_settings_group()
        layout.addWidget(self._cover_group)

        # Page size group
        self._size_group = self._create_page_size_group()
        layout.addWidget(self._size_group)

        # DPI settings group
        self._dpi_group = self._create_dpi_settings_group()
        layout.addWidget(self._dpi_group)

        # Buttons
        button_layout = self._create_button_layout()
        layout.addLayout(button_layout)

        self.setLayout(layout)

    def _create_page_selection_group(self) -> QGroupBox:
        """Create the page selection group."""
        group = QGroupBox("Select Page")
        layout = QVBoxLayout()

        # Page combo box
        self.page_combo = QComboBox()
        for i, page in enumerate(self.project.pages):
            page_label = self.project.get_page_display_name(page)
            if page.is_double_spread and not page.is_cover:
                page_label += " (Double Spread)"
            if page.manually_sized:
                page_label += " *"
            self.page_combo.addItem(page_label, i)
        layout.addWidget(self.page_combo)

        # Info label
        info_label = QLabel("* = Manually sized page")
        info_label.setStyleSheet("font-size: 9pt; color: gray;")
        layout.addWidget(info_label)

        group.setLayout(layout)
        return group

    def _create_cover_settings_group(self) -> QGroupBox:
        """Create the cover settings group."""
        group = QGroupBox("Cover Settings")
        layout = QVBoxLayout()

        # Cover checkbox
        self.cover_checkbox = QCheckBox("Designate as Cover")
        self.cover_checkbox.setToolTip("Mark this page as the book cover with wrap-around front/spine/back")
        layout.addWidget(self.cover_checkbox)

        # Paper thickness
        thickness_layout = QHBoxLayout()
        thickness_layout.addWidget(QLabel("Paper Thickness:"))
        self.thickness_spinbox = QDoubleSpinBox()
        self.thickness_spinbox.setRange(0.05, 1.0)
        self.thickness_spinbox.setSingleStep(0.05)
        self.thickness_spinbox.setValue(self.project.paper_thickness_mm)
        self.thickness_spinbox.setSuffix(" mm")
        self.thickness_spinbox.setToolTip("Thickness of paper for spine calculation")
        thickness_layout.addWidget(self.thickness_spinbox)
        layout.addLayout(thickness_layout)

        # Bleed margin
        bleed_layout = QHBoxLayout()
        bleed_layout.addWidget(QLabel("Bleed Margin:"))
        self.bleed_spinbox = QDoubleSpinBox()
        self.bleed_spinbox.setRange(0, 10)
        self.bleed_spinbox.setSingleStep(0.5)
        self.bleed_spinbox.setValue(self.project.cover_bleed_mm)
        self.bleed_spinbox.setSuffix(" mm")
        self.bleed_spinbox.setToolTip("Extra margin around cover for printing bleed")
        bleed_layout.addWidget(self.bleed_spinbox)
        layout.addLayout(bleed_layout)

        # Calculated spine width display
        self.spine_info_label = QLabel()
        self.spine_info_label.setStyleSheet("font-size: 9pt; color: #0066cc; padding: 5px;")
        self.spine_info_label.setWordWrap(True)
        layout.addWidget(self.spine_info_label)

        group.setLayout(layout)
        return group

    def _create_page_size_group(self) -> QGroupBox:
        """Create the page size group."""
        group = QGroupBox("Page Size")
        layout = QVBoxLayout()

        # Width
        width_layout = QHBoxLayout()
        width_layout.addWidget(QLabel("Width:"))
        self.width_spinbox = QDoubleSpinBox()
        self.width_spinbox.setRange(10, 1000)
        self.width_spinbox.setSuffix(" mm")
        width_layout.addWidget(self.width_spinbox)
        layout.addLayout(width_layout)

        # Height
        height_layout = QHBoxLayout()
        height_layout.addWidget(QLabel("Height:"))
        self.height_spinbox = QDoubleSpinBox()
        self.height_spinbox.setRange(10, 1000)
        self.height_spinbox.setSuffix(" mm")
        height_layout.addWidget(self.height_spinbox)
        layout.addLayout(height_layout)

        # Apply scope radio buttons
        scope_label = QLabel("Apply to:")
        layout.addWidget(scope_label)

        self._apply_scope_group = QButtonGroup(self)
        self.scope_page_only = QRadioButton("This page only")
        self.scope_non_manual = QRadioButton("All non-manual pages")
        self.scope_all_pages = QRadioButton("All pages (override manual sizing)")
        self.scope_page_only.setChecked(True)

        self._apply_scope_group.addButton(self.scope_page_only, 0)
        self._apply_scope_group.addButton(self.scope_non_manual, 1)
        self._apply_scope_group.addButton(self.scope_all_pages, 2)

        layout.addWidget(self.scope_page_only)
        layout.addWidget(self.scope_non_manual)
        layout.addWidget(self.scope_all_pages)

        group.setLayout(layout)
        return group

    def _create_dpi_settings_group(self) -> QGroupBox:
        """Create the DPI settings group."""
        group = QGroupBox("DPI Settings")
        layout = QVBoxLayout()

        # Working DPI
        working_dpi_layout = QHBoxLayout()
        working_dpi_layout.addWidget(QLabel("Working DPI:"))
        self.working_dpi_spinbox = QSpinBox()
        self.working_dpi_spinbox.setRange(72, 1200)
        self.working_dpi_spinbox.setValue(self.project.working_dpi)
        working_dpi_layout.addWidget(self.working_dpi_spinbox)
        layout.addLayout(working_dpi_layout)

        # Export DPI
        export_dpi_layout = QHBoxLayout()
        export_dpi_layout.addWidget(QLabel("Export DPI:"))
        self.export_dpi_spinbox = QSpinBox()
        self.export_dpi_spinbox.setRange(72, 1200)
        self.export_dpi_spinbox.setValue(self.project.export_dpi)
        export_dpi_layout.addWidget(self.export_dpi_spinbox)
        layout.addLayout(export_dpi_layout)

        group.setLayout(layout)
        return group

    def _create_button_layout(self) -> QHBoxLayout:
        """Create dialog button layout."""
        layout = QHBoxLayout()

        cancel_btn = QPushButton("Cancel")
        cancel_btn.clicked.connect(self.reject)

        ok_btn = QPushButton("OK")
        ok_btn.clicked.connect(self.accept)
        ok_btn.setDefault(True)

        layout.addStretch()
        layout.addWidget(cancel_btn)
        layout.addWidget(ok_btn)

        return layout

    def _connect_signals(self):
        """Connect widget signals to handlers."""
        self.page_combo.currentIndexChanged.connect(self._on_page_changed)
        self.cover_checkbox.stateChanged.connect(self._update_spine_info)
        self.thickness_spinbox.valueChanged.connect(self._update_spine_info)
        self.bleed_spinbox.valueChanged.connect(self._update_spine_info)

    def _initialize_values(self):
        """Initialize dialog values based on current page."""
        # Set initial page selection
        if 0 <= self.initial_page_index < len(self.project.pages):
            self.page_combo.setCurrentIndex(self.initial_page_index)

        # Trigger initial page change to populate values
        self._on_page_changed(self.initial_page_index)

    def _on_page_changed(self, index: int):
        """
        Handle page selection change.

        Args:
            index: Index of selected page
        """
        if index < 0 or index >= len(self.project.pages):
            return

        selected_page = self.project.pages[index]
        is_first_page = index == 0

        # Show/hide cover settings based on page selection
        self._cover_group.setVisible(is_first_page)

        # Update cover checkbox
        if is_first_page:
            self.cover_checkbox.setChecked(selected_page.is_cover)
            self._update_spine_info()

        # Get display width (accounting for double spreads and covers)
        if selected_page.is_cover:
            # For covers, show the full calculated width
            display_width = selected_page.layout.size[0]
        elif selected_page.is_double_spread:
            display_width = (
                selected_page.layout.base_width
                if hasattr(selected_page.layout, "base_width")
                else selected_page.layout.size[0] / 2
            )
        else:
            display_width = selected_page.layout.size[0]

        self.width_spinbox.setValue(display_width)
        self.height_spinbox.setValue(selected_page.layout.size[1])

        # Disable size editing for covers (auto-calculated)
        is_cover = selected_page.is_cover
        self.width_spinbox.setEnabled(not is_cover)
        self.height_spinbox.setEnabled(not is_cover)
        self.scope_non_manual.setEnabled(not is_cover)
        self.scope_all_pages.setEnabled(not is_cover)
        if is_cover:
            self.scope_page_only.setChecked(True)

    def _update_spine_info(self):
        """Update the spine information display."""
        if self.cover_checkbox.isChecked():
            # Calculate spine width with current settings
            content_pages = sum(p.get_page_count() for p in self.project.pages if not p.is_cover)
            sheets = math.ceil(content_pages / 4)
            spine_width = sheets * self.thickness_spinbox.value() * 2

            page_width = self.project.page_size_mm[0]
            total_width = (page_width * 2) + spine_width + (self.bleed_spinbox.value() * 2)

            self.spine_info_label.setText(
                f"Cover Layout: Front ({page_width:.0f}mm) + "
                f"Spine ({spine_width:.2f}mm) + "
                f"Back ({page_width:.0f}mm) + "
                f"Bleed ({self.bleed_spinbox.value():.1f}mm × 2)\n"
                f"Total Width: {total_width:.1f}mm | "
                f"Content Pages: {content_pages} | Sheets: {sheets}"
            )
        else:
            self.spine_info_label.setText("")

    def get_values(self) -> Dict[str, Any]:
        """
        Get dialog values.

        Returns:
            Dictionary containing all dialog values
        """
        selected_index = self.page_combo.currentData()
        selected_page = self.project.pages[selected_index]

        return {
            "selected_index": selected_index,
            "selected_page": selected_page,
            "is_cover": self.cover_checkbox.isChecked(),
            "paper_thickness_mm": self.thickness_spinbox.value(),
            "cover_bleed_mm": self.bleed_spinbox.value(),
            "width_mm": self.width_spinbox.value(),
            "height_mm": self.height_spinbox.value(),
            "working_dpi": self.working_dpi_spinbox.value(),
            "export_dpi": self.export_dpi_spinbox.value(),
            "apply_scope": self._apply_scope_group.checkedId(),
        }
