from __future__ import annotations

from xml.etree import ElementTree as ET
from typing import cast

import pytest
from openpyxl import Workbook
from openpyxl.worksheet.worksheet import Worksheet

from xml_to_xlsx import generate_workbook_from_xml
from xml_to_xlsx._renderer import _apply_column, _resolve_coordinates
from xml_to_xlsx._styling import (
    _map_horizontal,
    _map_vertical,
    _parse_border,
    _parse_font_family,
    apply_styles,
    apply_styles_to_range,
    normalize_color,
    parse_style_string,
)


def test_renderer_paths_for_style_merge_and_column_variants() -> None:
    xml = """
    <workbook>
      <sheet name="Paths" style="font-style:italic">
        <column column="D" width="9" />
        <row index="1" start-column="1">
          <style style="font-weight:bold">
            <cell>A</cell>
          </style>
          <merge range="A1:B1" />
        </row>
        <style style="color:#123456">
          <cell ref="C3" value="x" />
        </style>
        <cell ref="E5" merge="E5:F6" style="background-color:#fff0cc">m</cell>
        <merge range="H1:I1" />
      </sheet>
    </workbook>
    """

    wb = generate_workbook_from_xml(xml)
    ws = wb["Paths"]

    assert ws.column_dimensions["D"].width == 9
    ranges = {str(rng) for rng in ws.merged_cells.ranges}
    assert "A1:B1" in ranges
    assert "E5:F6" in ranges
    assert "H1:I1" in ranges
    assert ws["A1"].font.bold is True
    assert ws["C3"].font.italic is True


def test_renderer_coordinate_errors() -> None:
    with pytest.raises(ValueError, match="row reference"):
        _resolve_coordinates(ET.fromstring("<cell column='A' />"), None, None)

    with pytest.raises(ValueError, match="column reference"):
        _resolve_coordinates(ET.fromstring("<cell row='1' />"), None, None)


def test_apply_column_skips_when_width_missing() -> None:
    wb = Workbook()
    ws = cast(Worksheet, wb.active)
    _apply_column(ws, ET.fromstring("<column column='B' />"))
    assert ws.column_dimensions["B"].width in (None, 13.0)


def test_parse_style_and_color_edge_cases() -> None:
    parsed = parse_style_string("bad; font-weight: bold ; color:#abc ; empty:")
    assert parsed["font-weight"] == "bold"
    assert parsed["color"] == "#abc"

    assert normalize_color("") is None
    assert normalize_color("#abcd") is None
    assert normalize_color("custom-color") == "custom-color"


def test_apply_styles_and_border_parsing() -> None:
    wb = Workbook()
    ws = cast(Worksheet, wb.active)
    cell = ws["A1"]

    apply_styles(cell, {})
    apply_styles(
        cell,
        {
            "font-size": "13pt",
            "font-family": "'Times New Roman', serif",
            "text-decoration": "underline line-through",
            "background-color": "rgb(10,20,30)",
            "text-align": "centre",
            "vertical-align": "middle",
            "wrap-text": "pre-wrap",
            "text-rotation": "120",
            "shrink-to-fit": "yes",
            "number-format": "#,##0.00",
            "border-left": "dashed #999",
        },
    )

    assert "Times New Roman" in (cell.font.name or "")
    assert cell.font.underline == "single"
    assert cell.font.strike is True
    assert cell.fill.fgColor.rgb == "FF0A141E"
    assert cell.alignment.horizontal == "center"
    assert cell.alignment.vertical == "center"
    assert cell.alignment.wrap_text is True
    assert cell.alignment.text_rotation == 90
    assert cell.alignment.shrink_to_fit is True
    assert cell.number_format == "#,##0.00"
    assert cell.border.left.style == "dashed"

    side = _parse_border("0.5px")
    assert side is not None and side.style == "hair"
    side = _parse_border("3px")
    assert side is not None and side.style == "thick"
    assert _parse_border("   ") is None


def test_style_helpers_defaults_and_range_application() -> None:
    assert _map_horizontal("unknown") == "general"
    assert _map_vertical("unknown") == "bottom"
    assert _parse_font_family(" ,  ") is None
    assert _parse_font_family('"Calibri", sans-serif') == "Calibri"

    wb = Workbook()
    ws = cast(Worksheet, wb.active)
    apply_styles_to_range(
        ws.cell,
        {"font-weight": "bold", "border": "2px solid #111"},
        start_row=1,
        end_row=2,
        start_column=1,
        end_column=2,
    )
    assert ws["B2"].font.bold is True
    assert ws["B2"].border.left.style == "medium"

    apply_styles_to_range(
        ws.cell, {}, start_row=1, end_row=1, start_column=1, end_column=1
    )
