import json
from pathlib import Path

import pytest
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from sqlmodel import SQLModel

from datatypes.magicplan.api.response import MagicPlanPlan
from datatypes.magicplan.domain.mapper import map_plan
from datatypes.magicplan.domain.models import Plan

from backend.app.db.functions.magic_plan_functions import save_plan
from backend.app.db.models.magic_plan import (
    MagicPlanDoorModel,
    MagicPlanFloorModel,
    MagicPlanPlanModel,
    MagicPlanRoomModel,
    MagicPlanWindowModel,
)

FIXTURE_DIR = Path(__file__).parents[4] / "magic_plan"


@pytest.fixture(scope="module")
def domain_plan() -> Plan:
    data = json.loads(
        (FIXTURE_DIR / "magicplan_api_plan_response_example.json").read_text()
    )
    return map_plan(MagicPlanPlan.model_validate(data["data"]))


def _count(session: Session, model: type[SQLModel]) -> int:
    return session.execute(select(func.count()).select_from(model)).scalar_one()


def test_plan_row_present_after_save(db_session: Session, domain_plan: Plan) -> None:
    # Act
    save_plan(db_session, domain_plan)
    # Assert
    assert _count(db_session, MagicPlanPlanModel) == 1


def test_floor_count_matches_domain(db_session: Session, domain_plan: Plan) -> None:
    # Arrange
    expected = len(domain_plan.floors)
    # Act
    save_plan(db_session, domain_plan)
    # Assert
    assert _count(db_session, MagicPlanFloorModel) == expected


def test_room_count_matches_domain(db_session: Session, domain_plan: Plan) -> None:
    # Arrange
    expected = sum(len(f.rooms) for f in domain_plan.floors)
    # Act
    save_plan(db_session, domain_plan)
    # Assert
    assert _count(db_session, MagicPlanRoomModel) == expected


def test_window_count_matches_domain(db_session: Session, domain_plan: Plan) -> None:
    # Arrange
    expected = sum(len(r.windows) for f in domain_plan.floors for r in f.rooms)
    # Act
    save_plan(db_session, domain_plan)
    # Assert
    assert _count(db_session, MagicPlanWindowModel) == expected


def test_door_count_matches_domain(db_session: Session, domain_plan: Plan) -> None:
    # Arrange
    expected = sum(len(r.doors) for f in domain_plan.floors for r in f.rooms)
    # Act
    save_plan(db_session, domain_plan)
    # Assert
    assert _count(db_session, MagicPlanDoorModel) == expected


def test_save_plan_idempotent(db_session: Session, domain_plan: Plan) -> None:
    # Act — call twice within the same session
    save_plan(db_session, domain_plan)
    save_plan(db_session, domain_plan)
    # Assert — same row counts as a single call
    assert _count(db_session, MagicPlanPlanModel) == 1
    assert _count(db_session, MagicPlanFloorModel) == len(domain_plan.floors)
    assert _count(db_session, MagicPlanRoomModel) == sum(
        len(f.rooms) for f in domain_plan.floors
    )
    assert _count(db_session, MagicPlanWindowModel) == sum(
        len(r.windows) for f in domain_plan.floors for r in f.rooms
    )
    assert _count(db_session, MagicPlanDoorModel) == sum(
        len(r.doors) for f in domain_plan.floors for r in f.rooms
    )
