"""
給与所得の源泉徴収票 計算サービス
"""
from decimal import Decimal, ROUND_DOWN
from datetime import date
from typing import List, Optional, Dict, Any
import logging

from app.core.database import get_connection
from .schemas import WithholdingSlipData, DependentInfo

logger = logging.getLogger(__name__)

# 通勤手当の非課税限度額（月額150,000円）
COMMUTE_TAX_FREE_MONTHLY = Decimal("150000")


def calc_kyuyo_shotoku(annual_income: Decimal) -> Decimal:
    """
    給与所得控除後の金額を計算する（年末調整用）
    給与所得控除額を差し引いた後、1円未満切り捨て
    """
    income = int(annual_income)
    if income <= 550_000:
        # 給与所得は0
        return Decimal("0")
    elif income <= 1_625_000:
        kojo = 550_000
    elif income <= 1_800_000:
        # income * 40% - 100,000 だが1円単位で計算
        kojo = int(income * 0.4) - 100_000
    elif income <= 3_600_000:
        kojo = int(income * 0.3) + 80_000
    elif income <= 6_600_000:
        kojo = int(income * 0.2) + 440_000
    elif income <= 8_500_000:
        kojo = int(income * 0.1) + 1_100_000
    else:
        kojo = 1_950_000

    shotoku = max(0, income - kojo)
    return Decimal(str(shotoku))


def calc_basic_deduction(tax_year: int) -> Decimal:
    """
    基礎控除額を計算する
    令和7年(2025年)以降: 580,000円
    令和2年(2020年)〜令和6年(2024年): 480,000円
    """
    if tax_year >= 2025:
        return Decimal("580000")
    elif tax_year >= 2020:
        return Decimal("480000")
    else:
        return Decimal("380000")


def get_age_at_year_end(birth_date: date, tax_year: int) -> int:
    """その年の12月31日時点の年齢を計算"""
    year_end = date(tax_year, 12, 31)
    age = year_end.year - birth_date.year
    if (year_end.month, year_end.day) < (birth_date.month, birth_date.day):
        age -= 1
    return age


def calc_dependent_deduction(dep: Dict[str, Any], tax_year: int) -> tuple:
    """
    扶養控除額を計算する
    Returns: (deduction_type, deduction_amount)
    """
    birth_date = dep.get("birth_date")
    if birth_date is None:
        return ("一般", Decimal("380000"))

    age = get_age_at_year_end(birth_date, tax_year)

    if age >= 70:
        # 老人扶養親族 (同居老親等かどうかは不明なのでその他として扱う)
        return ("老人扶養", Decimal("480000"))
    elif 19 <= age <= 22:
        # 特定扶養親族
        return ("特定扶養", Decimal("630000"))
    elif age >= 16:
        # 一般扶養親族
        return ("一般扶養", Decimal("380000"))
    else:
        # 16歳未満 - 控除なし
        return ("年少", Decimal("0"))


def calc_spouse_deduction(spouse_income: Decimal, employee_income: Decimal, spouse_age: Optional[int]) -> tuple:
    """
    配偶者控除・配偶者特別控除を計算する
    Returns: (deduction_type, deduction_amount)
    """
    spouse_inc = int(spouse_income)
    emp_inc = int(employee_income)

    # 納税者の合計所得金額が1,000万円超の場合は配偶者控除なし
    # 給与所得として概算 (ここでは支払金額から控除後を算出)
    emp_shotoku = int(calc_kyuyo_shotoku(employee_income))
    if emp_shotoku > 10_000_000:
        return (None, Decimal("0"))

    # 配偶者の合計所得が48万円（収入103万円相当）以下 → 配偶者控除
    if spouse_inc <= 480_000:
        if spouse_age and spouse_age >= 70:
            return ("老人配偶者控除", Decimal("480000"))
        return ("配偶者控除", Decimal("380000"))

    # 配偶者特別控除（配偶者の合計所得 48万1円〜133万円）
    elif spouse_inc <= 1_330_000:
        # 段階的な配偶者特別控除
        if spouse_inc <= 950_000:
            return ("配偶者特別控除", Decimal("380000"))
        elif spouse_inc <= 1_000_000:
            return ("配偶者特別控除", Decimal("360000"))
        elif spouse_inc <= 1_050_000:
            return ("配偶者特別控除", Decimal("310000"))
        elif spouse_inc <= 1_100_000:
            return ("配偶者特別控除", Decimal("260000"))
        elif spouse_inc <= 1_150_000:
            return ("配偶者特別控除", Decimal("210000"))
        elif spouse_inc <= 1_200_000:
            return ("配偶者特別控除", Decimal("160000"))
        elif spouse_inc <= 1_250_000:
            return ("配偶者特別控除", Decimal("110000"))
        elif spouse_inc <= 1_300_000:
            return ("配偶者特別控除", Decimal("60000"))
        else:
            return ("配偶者特別控除", Decimal("30000"))
    else:
        return (None, Decimal("0"))


def get_withholding_slip_data(tax_year: int, employee_id: int) -> Optional[WithholdingSlipData]:
    """
    指定年度・従業員の源泉徴収票データを計算して返す
    """
    with get_connection() as conn:
        with conn.cursor() as cur:
            # 1. 従業員情報を取得
            cur.execute(
                """
                SELECT employee_id, employee_code, name, name_kana,
                       address, birth_date, my_number
                FROM employees
                WHERE employee_id = %s
                """,
                (employee_id,)
            )
            emp = cur.fetchone()
            if not emp:
                return None

            # 2. 月次給与データを集計（その年のすべての月）
            cur.execute(
                """
                SELECT
                    COUNT(*) as month_count,
                    COALESCE(SUM(total_payment), 0) as total_payment,
                    COALESCE(SUM(commute_allowance), 0) as total_commute,
                    COALESCE(SUM(health_insurance), 0) as health_ins,
                    COALESCE(SUM(care_insurance), 0) as care_ins,
                    COALESCE(SUM(pension_insurance), 0) as pension_ins,
                    COALESCE(SUM(employment_insurance), 0) as emp_ins,
                    COALESCE(SUM(income_tax), 0) as income_tax
                FROM monthly_payroll
                WHERE employee_id = %s
                  AND payroll_year = %s
                """,
                (employee_id, tax_year)
            )
            salary_summary = cur.fetchone()

            # 3. 賞与データを集計（その年のすべての賞与）
            cur.execute(
                """
                SELECT
                    COUNT(*) as bonus_count,
                    COALESCE(SUM(total_bonus), 0) as total_bonus,
                    COALESCE(SUM(health_insurance), 0) as health_ins,
                    COALESCE(SUM(care_insurance), 0) as care_ins,
                    COALESCE(SUM(pension_insurance), 0) as pension_ins,
                    COALESCE(SUM(employment_insurance), 0) as emp_ins,
                    COALESCE(SUM(income_tax), 0) as income_tax
                FROM bonus_payments
                WHERE employee_id = %s
                  AND bonus_year = %s
                """,
                (employee_id, tax_year)
            )
            bonus_summary = cur.fetchone()

            # 4. 扶養親族情報を取得（その年の12月31日時点で有効なもの）
            cur.execute(
                """
                SELECT d.dependent_id, d.name, d.relationship, d.birth_date,
                       d.is_spouse, d.is_disabled, d.income_amount, d.mynumber
                FROM dependents d
                WHERE d.employee_id = %s
                  AND d.valid_from <= %s
                  AND (d.valid_to IS NULL OR d.valid_to >= %s)
                ORDER BY d.is_spouse DESC, d.birth_date
                """,
                (employee_id,
                 date(tax_year, 12, 31),
                 date(tax_year, 12, 31))
            )
            dep_rows = cur.fetchall()

    # 5. 集計データを整理
    s = salary_summary or {}
    b = bonus_summary or {}

    def to_dec(v) -> Decimal:
        if v is None:
            return Decimal("0")
        return Decimal(str(v))

    salary_total = to_dec(s.get("total_payment", 0))
    salary_commute = to_dec(s.get("total_commute", 0))
    bonus_total = to_dec(b.get("total_bonus", 0))

    # 通勤手当の非課税額（月150,000円上限）は支払金額から除外
    # 簡易計算：通勤手当全額を非課税として除外（厳密には上限チェックが必要）
    commute_taxable = Decimal("0")  # 通勤手当は非課税として全額除外

    # 源泉徴収票の「支払金額」= 給与+賞与の総支給額（通勤手当を除く）
    # 実務上は通勤手当を除いた金額を記載することが多い
    total_pay_for_slip = salary_total - salary_commute + bonus_total

    # 社会保険料合計
    health_ins = to_dec(s.get("health_ins", 0)) + to_dec(b.get("health_ins", 0))
    care_ins = to_dec(s.get("care_ins", 0)) + to_dec(b.get("care_ins", 0))
    pension_ins = to_dec(s.get("pension_ins", 0)) + to_dec(b.get("pension_ins", 0))
    emp_ins = to_dec(s.get("emp_ins", 0)) + to_dec(b.get("emp_ins", 0))
    social_ins_total = health_ins + care_ins + pension_ins + emp_ins

    # 源泉徴収税額合計
    income_tax_total = to_dec(s.get("income_tax", 0)) + to_dec(b.get("income_tax", 0))

    # 6. 給与所得控除後の金額を計算
    kyuyo_shotoku = calc_kyuyo_shotoku(total_pay_for_slip)

    # 7. 基礎控除
    basic_deduction = calc_basic_deduction(tax_year)

    # 8. 扶養親族の処理
    birth_date_emp = emp.get("birth_date") if isinstance(emp, dict) else None

    dependents_over16 = []
    dependents_under16 = []
    dependent_deduction_total = Decimal("0")
    spouse_info = None
    spouse_deduction_amount = Decimal("0")
    has_spouse_deduction = False
    spouse_deduction_type = None

    for dep in dep_rows:
        if isinstance(dep, dict):
            d = dep
        else:
            d = {
                "dependent_id": dep[0], "name": dep[1], "relationship": dep[2],
                "birth_date": dep[3], "is_spouse": dep[4], "is_disabled": dep[5],
                "income_amount": dep[6], "mynumber": dep[7]
            }

        dep_birth = d.get("birth_date")
        dep_age = get_age_at_year_end(dep_birth, tax_year) if dep_birth else None
        dep_income = to_dec(d.get("income_amount", 0))

        if d.get("is_spouse"):
            # 配偶者処理
            spouse_age = dep_age
            sp_type, sp_amount = calc_spouse_deduction(dep_income, total_pay_for_slip, spouse_age)
            if sp_type:
                has_spouse_deduction = True
                spouse_deduction_amount = sp_amount
                spouse_deduction_type = sp_type
                spouse_deduction_total = sp_amount
            else:
                spouse_deduction_total = Decimal("0")

            spouse_info = DependentInfo(
                name=d.get("name", ""),
                relationship="配偶者",
                birth_date=str(dep_birth) if dep_birth else None,
                is_spouse=True,
                is_disabled=bool(d.get("is_disabled", False)),
                income_amount=dep_income,
                my_number=d.get("mynumber"),
                deduction_type=sp_type,
                deduction_amount=spouse_deduction_amount
            )
        else:
            # 扶養親族
            dep_type, dep_amount = calc_dependent_deduction(d, tax_year)
            dep_obj = DependentInfo(
                name=d.get("name", ""),
                relationship=d.get("relationship", ""),
                birth_date=str(dep_birth) if dep_birth else None,
                is_spouse=False,
                is_disabled=bool(d.get("is_disabled", False)),
                income_amount=dep_income,
                my_number=d.get("mynumber"),
                deduction_type=dep_type,
                deduction_amount=dep_amount
            )

            if dep_age is not None and dep_age < 16:
                dependents_under16.append(dep_obj)
            else:
                dependents_over16.append(dep_obj)
                dependent_deduction_total += dep_amount

    # 9. 所得控除の額の合計額
    total_deduction_amount = (
        social_ins_total
        + basic_deduction
        + spouse_deduction_amount
        + dependent_deduction_total
    )

    # 10. 摘要（社会保険料の内訳）
    remarks_parts = []
    if health_ins > 0:
        remarks_parts.append(f"健康保険料{int(health_ins):,}円")
    if care_ins > 0:
        remarks_parts.append(f"介護保険料{int(care_ins):,}円")
    if pension_ins > 0:
        remarks_parts.append(f"厚生年金{int(pension_ins):,}円")
    if emp_ins > 0:
        remarks_parts.append(f"雇用保険{int(emp_ins):,}円")
    remarks = "　".join(remarks_parts) if remarks_parts else None

    months_with_salary = int(s.get("month_count", 0)) if s else 0
    months_with_bonus = int(b.get("bonus_count", 0)) if b else 0

    return WithholdingSlipData(
        employee_id=emp.get("employee_id") if isinstance(emp, dict) else emp[0],
        employee_code=emp.get("employee_code", "") if isinstance(emp, dict) else emp[1],
        employee_name=emp.get("name", "") if isinstance(emp, dict) else emp[2],
        employee_name_kana=emp.get("name_kana") if isinstance(emp, dict) else emp[3],
        employee_address=emp.get("address") if isinstance(emp, dict) else emp[4],
        employee_birth_date=str(emp.get("birth_date")) if (isinstance(emp, dict) and emp.get("birth_date")) else None,
        my_number=emp.get("my_number") if isinstance(emp, dict) else emp[6],
        tax_year=tax_year,
        total_payment=salary_total + bonus_total,
        total_payment_excl_commute=total_pay_for_slip,
        kyuyo_shotoku=kyuyo_shotoku,
        social_insurance_total=social_ins_total,
        health_insurance_total=health_ins,
        care_insurance_total=care_ins,
        pension_total=pension_ins,
        employment_insurance_total=emp_ins,
        basic_deduction=basic_deduction,
        has_spouse_deduction=has_spouse_deduction,
        spouse_deduction_amount=spouse_deduction_amount,
        spouse_name=spouse_info.name if spouse_info else None,
        spouse_name_kana=spouse_info.name_kana if spouse_info else None,
        spouse_income=spouse_info.income_amount if spouse_info else Decimal("0"),
        spouse_deduction_type=spouse_deduction_type,
        dependents_over16=dependents_over16,
        dependents_under16=dependents_under16,
        dependent_deduction_total=dependent_deduction_total,
        total_deduction_amount=total_deduction_amount,
        income_tax_withheld=income_tax_total,
        months_with_salary=months_with_salary,
        months_with_bonus=months_with_bonus,
        remarks=remarks,
    )


def get_withholding_slips(tax_year: int, employee_ids: List[int]) -> List[WithholdingSlipData]:
    """複数従業員の源泉徴収票データを取得"""
    results = []
    for eid in employee_ids:
        try:
            slip = get_withholding_slip_data(tax_year, eid)
            if slip:
                results.append(slip)
        except Exception as e:
            logger.error(f"源泉徴収票計算エラー employee_id={eid}: {e}")
    return results
