"""
Database 단위 테스트 — 마이그레이션, 동기화, 헬퍼.
"""
import os
import sys
import tempfile
from datetime import date

import pytest

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from core.database import Database


@pytest.fixture
def fresh_db(tmp_path):
    """매 테스트마다 빈 DB."""
    return Database(str(tmp_path / "test.db"))


class TestSettingsHelpers:
    def test_get_setting_int_valid(self, fresh_db):
        fresh_db.set_setting('foo', '42')
        assert fresh_db.get_setting_int('foo') == 42

    def test_get_setting_int_invalid_returns_default(self, fresh_db):
        fresh_db.set_setting('foo', 'abc')
        assert fresh_db.get_setting_int('foo', 99) == 99

    def test_get_setting_int_missing_returns_default(self, fresh_db):
        assert fresh_db.get_setting_int('missing', 7) == 7

    def test_get_setting_bool_truthy(self, fresh_db):
        fresh_db.set_setting('flag', 'true')
        assert fresh_db.get_setting_bool('flag') is True

    def test_get_setting_bool_falsy(self, fresh_db):
        fresh_db.set_setting('flag', 'no')
        assert fresh_db.get_setting_bool('flag') is False

    def test_get_setting_float(self, fresh_db):
        fresh_db.set_setting('rate', '3.14')
        assert fresh_db.get_setting_float('rate') == 3.14


class TestSettingsAutoSync:
    def test_work_minutes_to_work_hours_floor(self, fresh_db):
        """work_minutes 저장 시 work_hours는 floor 동기화 (450 → 7)"""
        fresh_db.save_settings({'work_minutes': 450})
        assert fresh_db.get_setting('work_hours') == '7'

    def test_work_hours_to_work_minutes(self, fresh_db):
        fresh_db.save_settings({'work_hours': 8})
        assert fresh_db.get_setting('work_minutes') == '480'

    def test_annual_leave_bidirectional(self, fresh_db):
        fresh_db.save_settings({'annual_leave_days': 12})
        assert fresh_db.get_setting('annual_leave_total') == '12'


class TestWorkMinutes:
    def test_get_work_minutes_default(self, fresh_db):
        assert fresh_db.get_work_minutes() == 480

    def test_get_work_minutes_after_save(self, fresh_db):
        fresh_db.save_settings({'work_minutes': 450})
        assert fresh_db.get_work_minutes() == 450


class TestLeaveCalculation:
    def test_leave_minutes_for_short_worker(self, fresh_db):
        """단축근무자(7h30m) 1일 연차 = 450분"""
        fresh_db.save_settings({'work_minutes': 450})
        today = date.today().isoformat()
        fresh_db.add_leave_record(today, 'annual', 1.0)
        assert fresh_db.get_today_leave_minutes() == 450

    def test_half_day_leave(self, fresh_db):
        today = date.today().isoformat()
        fresh_db.add_leave_record(today, 'half', 0.5)
        assert fresh_db.get_today_leave_minutes() == 240  # 8h * 0.5


class TestMigrationIdempotency:
    def test_annual_leave_keys_migrated_sentinel(self, fresh_db):
        assert fresh_db.get_setting('annual_leave_keys_migrated') == 'true'

    def test_re_init_does_not_break(self, tmp_path):
        path = str(tmp_path / "test.db")
        db1 = Database(path)
        db1.save_settings({'work_minutes': 450})
        # 두 번째 init
        db2 = Database(path)
        assert db2.get_work_minutes() == 450


class TestConsecutiveOvertimeDays:
    def test_no_records(self, fresh_db):
        assert fresh_db.get_consecutive_overtime_days() == 0

    def test_three_consecutive(self, fresh_db):
        from datetime import date, timedelta
        today = date.today()
        for i in range(3):
            d = (today - timedelta(days=i)).isoformat()
            fresh_db.add_work_record(d, '09:00:00')
            fresh_db.update_clock_out(d, '20:00:00', total_hours=11.0,
                                       overtime_minutes=120, overtime_earned=120)
        assert fresh_db.get_consecutive_overtime_days() == 3


class TestLeaveQueriesByDate:
    def test_get_leave_minutes_for_no_records(self, fresh_db):
        assert fresh_db.get_leave_minutes_for('2026-05-01') == 0

    def test_full_day_leave_detected(self, fresh_db):
        fresh_db.add_leave_record('2026-05-15', '연차', 1.0, '여행')
        assert fresh_db.has_full_day_leave('2026-05-15')
        assert fresh_db.get_leave_minutes_for('2026-05-15') == 480

    def test_half_day_not_full(self, fresh_db):
        fresh_db.add_leave_record('2026-05-15', '반차', 0.5)
        assert not fresh_db.has_full_day_leave('2026-05-15')
        assert fresh_db.get_leave_minutes_for('2026-05-15') == 240

    def test_two_halves_become_full(self, fresh_db):
        fresh_db.add_leave_record('2026-05-15', '오전반차', 0.5)
        fresh_db.add_leave_record('2026-05-15', '오후반차', 0.5)
        assert fresh_db.has_full_day_leave('2026-05-15')
        assert fresh_db.get_leave_minutes_for('2026-05-15') == 480

    def test_records_by_date(self, fresh_db):
        fresh_db.add_leave_record('2026-05-15', '연차', 1.0, '메모')
        recs = fresh_db.get_leave_records_by_date('2026-05-15')
        assert len(recs) == 1
        assert recs[0]['leave_type'] == '연차'
        assert recs[0]['memo'] == '메모'

    def test_records_by_range(self, fresh_db):
        fresh_db.add_leave_record('2026-05-01', '연차', 1.0)
        fresh_db.add_leave_record('2026-05-10', '반차', 0.5)
        fresh_db.add_leave_record('2026-06-01', '연차', 1.0)
        recs = fresh_db.get_leave_records_by_range('2026-05-01', '2026-05-31')
        assert len(recs) == 2
        # 날짜 정렬
        assert recs[0]['date'] == '2026-05-01'
        assert recs[1]['date'] == '2026-05-10'


class TestRecurringLeavesDB:
    def test_add_and_list(self, fresh_db):
        rid = fresh_db.add_recurring_leave(
            'weekly:friday', '반차', 0.5, '2026-05-01', '2026-12-31', '단축'
        )
        assert rid > 0
        recs = fresh_db.get_recurring_leaves()
        assert len(recs) == 1
        assert recs[0]['pattern'] == 'weekly:friday'
        assert recs[0]['memo'] == '단축'

    def test_active_on_filter(self, fresh_db):
        # 종료일이 지난 패턴
        fresh_db.add_recurring_leave('weekly:fri', '반차', 0.5,
                                       '2025-01-01', '2025-12-31')
        # 아직 시작 안 한 패턴
        fresh_db.add_recurring_leave('weekly:mon', '반차', 0.5,
                                       '2027-01-01', None)
        # 현재 활성 패턴
        fresh_db.add_recurring_leave('monthly:15', '연차', 1.0,
                                       '2026-01-01', None)
        active = fresh_db.get_recurring_leaves(active_on='2026-05-15')
        assert len(active) == 1
        assert active[0]['pattern'] == 'monthly:15'

    def test_delete(self, fresh_db):
        rid = fresh_db.add_recurring_leave('weekly:fri', '반차', 0.5,
                                              '2026-01-01')
        assert len(fresh_db.get_recurring_leaves()) == 1
        fresh_db.delete_recurring_leave(rid)
        assert fresh_db.get_recurring_leaves() == []

    def test_recurring_contributes_to_leave_minutes(self, fresh_db):
        # 매주 금요일 반차
        fresh_db.add_recurring_leave('weekly:friday', '반차', 0.5,
                                       '2026-01-01')
        # 2026-05-01 = Friday → 240분
        assert fresh_db.get_leave_minutes_for('2026-05-01') == 240
        # 2026-05-04 = Monday → 0분
        assert fresh_db.get_leave_minutes_for('2026-05-04') == 0

    def test_concrete_plus_recurring_sum(self, fresh_db):
        # 매주 금요일 반차 + 그날 별도 반반차 추가
        fresh_db.add_recurring_leave('weekly:friday', '반차', 0.5, '2026-01-01')
        fresh_db.add_leave_record('2026-05-01', '반반차', 0.25)
        # 0.5 + 0.25 = 0.75일 = 360분
        assert fresh_db.get_leave_minutes_for('2026-05-01') == 360
        assert not fresh_db.has_full_day_leave('2026-05-01')

    def test_concrete_plus_recurring_full_day(self, fresh_db):
        fresh_db.add_recurring_leave('weekly:friday', '반차', 0.5, '2026-01-01')
        fresh_db.add_leave_record('2026-05-01', '오후반차', 0.5)
        # 0.5 + 0.5 = 1.0일
        assert fresh_db.has_full_day_leave('2026-05-01')
