"""Unit tests for routine API endpoints."""

import pytest
from datetime import datetime, timezone
from tinydb import Query
from models.routine import Routine
from models.routine_item import RoutineItem
from models.routine_schedule import RoutineSchedule
from models.routine_extension import RoutineExtension
from models.pending_confirmation import PendingConfirmation
from api.error_codes import ErrorCodes
from db.db import routine_db, routine_items_db, routine_schedules_db, routine_extensions_db, pending_confirmations_db


class TestRoutineModel:
    """Test Routine model serialization and validation."""

    def test_routine_creation(self):
        """Test creating a routine instance."""
        routine = Routine(name="Morning Routine", points=50, image_id="img123", user_id="user1")
        assert routine.name == "Morning Routine"
        assert routine.points == 50
        assert routine.image_id == "img123"
        assert routine.user_id == "user1"
        assert routine.id is not None

    def test_routine_to_dict(self):
        """Test routine serialization."""
        routine = Routine(name="Test", points=10, image_id=None, user_id="user1")
        data = routine.to_dict()
        assert data["name"] == "Test"
        assert data["points"] == 10
        assert data["id"] == routine.id

    def test_routine_from_dict(self):
        """Test routine deserialization."""
        routine_dict = {"id": "r1", "name": "Test", "points": 20, "image_id": "img1", "user_id": "u1"}
        routine = Routine.from_dict(routine_dict)
        assert routine.id == "r1"
        assert routine.name == "Test"
        assert routine.points == 20


class TestRoutineItemModel:
    """Test RoutineItem model."""

    def test_routine_item_creation(self):
        """Test creating a routine item."""
        item = RoutineItem(routine_id="r1", name="Make Bed", image_id=None, order=0)
        assert item.routine_id == "r1"
        assert item.name == "Make Bed"
        assert item.order == 0

    def test_routine_item_to_dict(self):
        """Test item serialization."""
        item = RoutineItem(routine_id="r1", name="Get Dressed", image_id="img1", order=1)
        data = item.to_dict()
        assert data["routine_id"] == "r1"
        assert data["name"] == "Get Dressed"
        assert data["order"] == 1


class TestRoutineScheduleModel:
    """Test RoutineSchedule model."""

    def test_days_mode_schedule_creation(self):
        """Test creating days mode schedule."""
        day_configs = [
            {"day": 0, "hour": 8, "minute": 0},
            {"day": 1, "hour": 9, "minute": 30},
        ]
        schedule = RoutineSchedule(
            child_id="c1",
            routine_id="r1",
            mode="days",
            enabled=True,
            day_configs=day_configs,
            default_hour=8,
            default_minute=0,
            default_has_deadline=True,
        )
        assert schedule.mode == "days"
        assert len(schedule.day_configs) == 2
        assert schedule.enabled is True

    def test_interval_mode_schedule_creation(self):
        """Test creating interval mode schedule."""
        schedule = RoutineSchedule(
            child_id="c1",
            routine_id="r1",
            mode="interval",
            enabled=True,
            interval_days=3,
            anchor_date="2026-05-01",
            interval_has_deadline=True,
            interval_hour=10,
            interval_minute=30,
        )
        assert schedule.mode == "interval"
        assert schedule.interval_days == 3
        assert schedule.interval_hour == 10


class TestRoutineExtensionModel:
    """Test RoutineExtension model."""

    def test_extension_creation(self):
        """Test creating a routine extension."""
        extension = RoutineExtension(child_id="c1", routine_id="r1", date="2026-05-10")
        assert extension.child_id == "c1"
        assert extension.routine_id == "r1"
        assert extension.date == "2026-05-10"


class TestRoutineDB:
    """Test routine database operations."""

    def setup_method(self):
        """Clear routine db before each test."""
        routine_db.truncate()

    def test_add_routine(self):
        """Test adding a routine to database."""
        routine = Routine(name="Test Routine", points=50, image_id=None, user_id="user1")
        routine_db.insert(routine.to_dict())
        
        result = routine_db.search(Query().id == routine.id)
        assert len(result) == 1
        assert result[0]["name"] == "Test Routine"

    def test_get_routine_by_id(self):
        """Test fetching routine by ID."""
        routine = Routine(name="Fetch Test", points=30, image_id=None, user_id="user1")
        routine_db.insert(routine.to_dict())
        
        result = routine_db.get(Query().id == routine.id)
        assert result is not None
        assert result["name"] == "Fetch Test"

    def test_list_user_routines(self):
        """Test listing routines for a user."""
        r1 = Routine(name="R1", points=10, image_id=None, user_id="user1")
        r2 = Routine(name="R2", points=20, image_id=None, user_id="user1")
        r3 = Routine(name="R3", points=15, image_id=None, user_id="user2")
        
        routine_db.insert(r1.to_dict())
        routine_db.insert(r2.to_dict())
        routine_db.insert(r3.to_dict())
        
        results = routine_db.search(Query().user_id == "user1")
        assert len(results) == 2

    def test_update_routine(self):
        """Test updating a routine."""
        routine = Routine(name="Original", points=50, image_id=None, user_id="user1")
        routine_db.insert(routine.to_dict())
        
        routine_db.update({"name": "Updated", "points": 100}, Query().id == routine.id)
        result = routine_db.get(Query().id == routine.id)
        assert result["name"] == "Updated"
        assert result["points"] == 100


class TestRoutineItemDB:
    """Test routine item database operations."""

    def setup_method(self):
        """Clear item db before each test."""
        routine_items_db.truncate()

    def test_add_routine_item(self):
        """Test adding a routine item."""
        item = RoutineItem(routine_id="r1", name="Make Bed", image_id=None, order=0)
        routine_items_db.insert(item.to_dict())
        
        result = routine_items_db.search(Query().routine_id == "r1")
        assert len(result) == 1
        assert result[0]["name"] == "Make Bed"

    def test_get_items_for_routine(self):
        """Test fetching all items for a routine."""
        i1 = RoutineItem(routine_id="r1", name="Item1", image_id=None, order=0)
        i2 = RoutineItem(routine_id="r1", name="Item2", image_id=None, order=1)
        i3 = RoutineItem(routine_id="r2", name="Item3", image_id=None, order=0)
        
        routine_items_db.insert(i1.to_dict())
        routine_items_db.insert(i2.to_dict())
        routine_items_db.insert(i3.to_dict())
        
        results = routine_items_db.search(Query().routine_id == "r1")
        assert len(results) == 2

    def test_delete_item(self):
        """Test deleting a routine item."""
        item = RoutineItem(routine_id="r1", name="Test", image_id=None, order=0)
        routine_items_db.insert(item.to_dict())
        
        routine_items_db.remove(Query().id == item.id)
        result = routine_items_db.search(Query().id == item.id)
        assert len(result) == 0


class TestRoutineScheduleDB:
    """Test routine schedule database operations."""

    def setup_method(self):
        """Clear schedule db before each test."""
        routine_schedules_db.truncate()

    def test_upsert_schedule(self):
        """Test upserting a routine schedule."""
        day_configs = [{"day": 0, "hour": 8, "minute": 0}]
        schedule = RoutineSchedule(
            child_id="c1",
            routine_id="r1",
            mode="days",
            enabled=True,
            day_configs=day_configs,
            default_hour=8,
            default_minute=0,
            default_has_deadline=True,
        )
        routine_schedules_db.insert(schedule.to_dict())
        
        result = routine_schedules_db.get(
            (Query().child_id == "c1") & (Query().routine_id == "r1")
        )
        assert result is not None
        assert result["mode"] == "days"

    def test_delete_schedule(self):
        """Test deleting a schedule."""
        schedule = RoutineSchedule(
            child_id="c1",
            routine_id="r1",
            mode="interval",
            enabled=True,
            interval_days=2,
            anchor_date="2026-05-01",
            interval_has_deadline=True,
            interval_hour=10,
            interval_minute=0,
        )
        routine_schedules_db.insert(schedule.to_dict())
        
        routine_schedules_db.remove(
            (Query().child_id == "c1") & (Query().routine_id == "r1")
        )
        result = routine_schedules_db.search(Query().child_id == "c1")
        assert len(result) == 0


class TestPendingRoutineConfirmation:
    """Test pending routine confirmation workflow."""

    def setup_method(self):
        """Clear db before each test."""
        pending_confirmations_db.truncate()

    def test_create_routine_confirmation(self):
        """Test creating a pending routine confirmation."""
        confirmation = PendingConfirmation(
            child_id="c1",
            entity_id="r1",
            entity_type="routine",
            user_id="u1",
            status="pending",
        )
        pending_confirmations_db.insert(confirmation.to_dict())
        
        result = pending_confirmations_db.get(
            (Query().child_id == "c1") & (Query().entity_id == "r1") & (Query().entity_type == "routine")
        )
        assert result is not None
        assert result["status"] == "pending"

    def test_approve_routine_confirmation(self):
        """Test approving a routine confirmation."""
        confirmation = PendingConfirmation(
            child_id="c1",
            entity_id="r1",
            entity_type="routine",
            user_id="u1",
            status="pending",
        )
        pending_confirmations_db.insert(confirmation.to_dict())
        
        today_utc = datetime.now(timezone.utc).isoformat()
        pending_confirmations_db.update(
            {"status": "approved", "approved_at": today_utc},
            Query().id == confirmation.id,
        )
        
        result = pending_confirmations_db.get(Query().id == confirmation.id)
        assert result["status"] == "approved"
        assert result["approved_at"] is not None
