from __future__ import annotations

from io import BytesIO
from textwrap import dedent

import pandas as pd
import pytest

from services.importers import ImportResult, load_project_imports, load_scenario_imports
from schemas.imports import ProjectImportRow, ScenarioImportRow


def test_load_project_imports_from_csv() -> None:
    csv_content = (
        "name,location,operation_type,description\n"
        "Project A,Chile,open pit,First project\n"
        "Project B,,underground,Second project\n"
    )
    stream = BytesIO(csv_content.encode("utf-8"))

    result = load_project_imports(stream, "projects.csv")

    assert isinstance(result, ImportResult)
    assert len(result.rows) == 2
    assert not result.errors
    first = result.rows[0]
    assert first.row_number == 2
    assert isinstance(first.data, ProjectImportRow)
    assert first.data.name == "Project A"
    assert first.data.operation_type.value == "open_pit"
    second = result.rows[1]
    assert second.row_number == 3
    assert isinstance(second.data, ProjectImportRow)
    assert second.data.location is None


def test_load_scenario_imports_from_excel() -> None:
    df = pd.DataFrame(
        [
            {
                "project_name": "Project A",
                "name": "Scenario 1",
                "status": "Active",
                "start_date": "2025-01-01",
                "end_date": "2025-12-31",
                "discount_rate": "7.5%",
                "currency": "usd",
                "primary_resource": "Electricity",
            }
        ]
    )
    buffer = BytesIO()
    df.to_excel(buffer, index=False)
    buffer.seek(0)

    result = load_scenario_imports(buffer, "scenarios.xlsx")

    assert len(result.rows) == 1
    assert not result.errors
    row = result.rows[0]
    assert row.row_number == 2
    assert isinstance(row.data, ScenarioImportRow)
    assert row.data.status.value == "active"
    assert row.data.currency == "USD"
    assert row.data.discount_rate == pytest.approx(7.5)


def test_import_errors_include_row_numbers() -> None:
    csv_content = "name,operation_type\n,open pit\n"
    stream = BytesIO(csv_content.encode("utf-8"))

    result = load_project_imports(stream, "projects.csv")

    assert len(result.rows) == 0
    assert len(result.errors) == 1
    error = result.errors[0]
    assert error.row_number == 2
    assert error.field == "name"
    assert "required" in error.message


def test_project_import_handles_missing_columns() -> None:
    csv_content = "name\nProject Only\n"
    stream = BytesIO(csv_content.encode("utf-8"))

    result = load_project_imports(stream, "projects.csv")

    assert result.rows == []
    assert len(result.errors) == 1
    error = result.errors[0]
    assert error.row_number == 2
    assert error.field == "operation_type"


def test_project_import_rejects_invalid_operation_type() -> None:
    csv_content = "name,operation_type\nProject X,unknown\n"
    stream = BytesIO(csv_content.encode("utf-8"))

    result = load_project_imports(stream, "projects.csv")

    assert len(result.rows) == 0
    assert len(result.errors) == 1
    error = result.errors[0]
    assert error.row_number == 2
    assert error.field == "operation_type"


def test_scenario_import_flags_invalid_dates() -> None:
    csv_content = dedent(
        """
        project_name,name,status,start_date,end_date
        Project A,Scenario Reverse,Draft,2025-12-31,2025-01-01
        """
    ).strip()
    stream = BytesIO(csv_content.encode("utf-8"))

    result = load_scenario_imports(stream, "scenarios.csv")

    assert len(result.rows) == 0
    assert len(result.errors) == 1
    error = result.errors[0]
    assert error.row_number == 2
    assert error.field is None


def test_scenario_import_handles_large_dataset() -> None:
    buffer = BytesIO()
    df = pd.DataFrame(
        {
            "project_name": ["Project"] * 500,
            "name": [f"Scenario {i}" for i in range(500)],
            "status": ["draft"] * 500,
        }
    )
    df.to_csv(buffer, index=False)
    buffer.seek(0)

    result = load_scenario_imports(buffer, "bulk.csv")

    assert len(result.rows) == 500
    assert len(result.rows) == 500


def test_scenario_import_rejects_invalid_currency() -> None:
    csv_content = dedent(
        """
        project_name,name,currency
        Project A,Scenario Invalid,US
        """
    ).strip()
    stream = BytesIO(csv_content.encode("utf-8"))

    result = load_scenario_imports(stream, "scenarios.csv")

    assert not result.rows
    assert result.errors
    error = result.errors[0]
    assert error.row_number == 2
    assert error.field == "currency"
    assert "Invalid currency code" in error.message
