"""
給与計算サービス (Payroll Calculation Service)
"""
from decimal import Decimal
from datetime import date
from typing import Dict, Any
from ...core.database import get_connection


class PayrollCalculationService:
    """給与計算サービス"""
    
    @staticmethod
    def get_active_dependents_count(employee_id: int, target_date: date) -> int:
        """有効な扶養家族数を取得（源泉税控除対象：16歳以上）"""
        with get_connection() as conn:
            with conn.cursor() as cur:
                # その年の12月31日時点で16歳以上の扶養家族をカウント
                # 源泉所得税の扶養控除対象は16歳以上が対象
                print(f"[DEBUG] 扶養人数取得: 従業員ID={employee_id}, 対象日={target_date}")
                
                cur.execute(
                    """
                    SELECT COUNT(*) as count FROM dependents 
                    WHERE employee_id = %s
                    AND valid_from <= %s
                    AND (valid_to IS NULL OR valid_to >= %s)
                    AND birth_date IS NOT NULL
                    AND EXTRACT(YEAR FROM AGE(DATE(EXTRACT(YEAR FROM %s) || '-12-31'), birth_date)) >= 16
                    """,
                    (employee_id, target_date, target_date, target_date)
                )
                result = cur.fetchone()
                count = result["count"] if result else 0
                print(f"[DEBUG] 取得した扶養人数: {count}人")
                return count
    
    @staticmethod
    def get_salary_setting(employee_id: int, target_date: date) -> Dict[str, Any]:
        """給与設定を取得"""
        with get_connection() as conn:
            with conn.cursor() as cur:
                cur.execute(
                    """
                    SELECT * FROM salary_settings 
                    WHERE employee_id = %s 
                    AND valid_from <= %s
                    AND (valid_to IS NULL OR valid_to >= %s)
                    ORDER BY valid_from DESC
                    LIMIT 1
                    """,
                    (employee_id, target_date, target_date)
                )
                return cur.fetchone()
    
    @staticmethod
    def get_insurance_rate(rate_type: str, target_date: date, calculation_target: str = '給与') -> Dict[str, Any]:
        """保険料率を取得（年度がない場合は前年度を参照）"""
        with get_connection() as conn:
            with conn.cursor() as cur:
                # rate_yearで年度を判定
                target_year = target_date.year
                cur.execute(
                    """
                    SELECT * FROM insurance_rates 
                    WHERE rate_type = %s
                    AND rate_year = %s
                    AND calculation_target = %s
                    ORDER BY rate_id DESC
                    LIMIT 1
                    """,
                    (rate_type, target_year, calculation_target)
                )
                result = cur.fetchone()
                
                # 現在の年度でデータが見つからない場合は前年度を検索（最大3年遡る）
                if not result:
                    for year_offset in range(1, 4):
                        previous_year = target_year - year_offset
                        cur.execute(
                            """
                            SELECT * FROM insurance_rates 
                            WHERE rate_type = %s
                            AND rate_year = %s
                            AND calculation_target = %s
                            ORDER BY rate_id DESC
                            LIMIT 1
                            """,
                            (rate_type, previous_year, calculation_target)
                        )
                        result = cur.fetchone()
                        if result:
                            break
                
                return result
    
    @staticmethod
    def get_insurance_limit(setting_type: str, target_date: date) -> Decimal:
        """社会保険料上限を取得"""
        with get_connection() as conn:
            with conn.cursor() as cur:
                cur.execute(
                    """
                    SELECT limit_amount FROM insurance_limit_settings 
                    WHERE setting_type = %s
                    AND valid_from <= %s
                    AND (valid_to IS NULL OR valid_to >= %s)
                    ORDER BY valid_from DESC
                    LIMIT 1
                    """,
                    (setting_type, target_date, target_date)
                )
                result = cur.fetchone()
                return Decimal(str(result["limit_amount"])) if result else Decimal("0")
    
    @staticmethod
    def calculate_income_tax(
        monthly_income: Decimal, 
        dependents_count: int, 
        target_date: date
    ) -> Decimal:
        """所得税を計算（税年度がない場合は前年度の表を参照）"""
        with get_connection() as conn:
            with conn.cursor() as cur:
                # 現在の年度で検索
                # 月収がmonthly_income_from <= monthly_income < monthly_income_toの範囲に入るか、
                # または月収がmonthly_income_from <= monthly_income <= monthly_income_toの範囲（Toが同値の場合も含む）
                print(f"[DEBUG] 所得税検索: 月収={monthly_income}, 扶養人数={dependents_count}, 日付={target_date}")
                
                cur.execute(
                    """
                    SELECT tax_id, monthly_income_from, monthly_income_to, tax_amount, valid_from 
                    FROM income_tax_table 
                    WHERE valid_from <= %s
                    AND (valid_to IS NULL OR valid_to >= %s)
                    AND dependents_count = %s
                    AND monthly_income_from <= %s
                    AND monthly_income_to > %s
                    ORDER BY valid_from DESC
                    LIMIT 1
                    """,
                    (target_date, target_date, dependents_count, monthly_income, monthly_income)
                )
                result = cur.fetchone()
                
                if result:
                    print(f"[DEBUG] 見つかった（範囲検索）: From={result['monthly_income_from']}, To={result['monthly_income_to']}, 税額={result['tax_amount']}")
                
                # 見つからない場合は、Toが同値のケースも検索（月収がちょうどToの値に達する場合）
                if not result:
                    cur.execute(
                        """
                        SELECT tax_id, monthly_income_from, monthly_income_to, tax_amount, valid_from 
                        FROM income_tax_table 
                        WHERE valid_from <= %s
                        AND (valid_to IS NULL OR valid_to >= %s)
                        AND dependents_count = %s
                        AND monthly_income_from <= %s
                        AND monthly_income_to = %s
                        ORDER BY valid_from DESC
                        LIMIT 1
                        """,
                        (target_date, target_date, dependents_count, monthly_income, monthly_income)
                    )
                    result = cur.fetchone()
                    if result:
                        print(f"[DEBUG] 見つかった（To同値検索）: From={result['monthly_income_from']}, To={result['monthly_income_to']}, 税額={result['tax_amount']}")
                
                # データが見つからない場合は、指定された日付に関係なく前年度以前の表を検索
                if not result:
                    for year_offset in range(1, 4):
                        previous_date = date(target_date.year - year_offset, target_date.month, target_date.day)
                        print(f"[DEBUG] 前年度検索: {previous_date}")
                        
                        cur.execute(
                            """
                            SELECT tax_id, monthly_income_from, monthly_income_to, tax_amount, valid_from 
                            FROM income_tax_table 
                            WHERE valid_from <= %s
                            AND (valid_to IS NULL OR valid_to >= %s)
                            AND dependents_count = %s
                            AND monthly_income_from <= %s
                            AND monthly_income_to > %s
                            ORDER BY valid_from DESC
                            LIMIT 1
                            """,
                            (previous_date, previous_date, dependents_count, monthly_income, monthly_income)
                        )
                        result = cur.fetchone()
                        if result:
                            print(f"[DEBUG] 前年度で見つかった: {previous_date}, 税額={result['tax_amount']}")
                            break
                        
                        # Toが同値のケースも検索
                        cur.execute(
                            """
                            SELECT tax_id, monthly_income_from, monthly_income_to, tax_amount, valid_from 
                            FROM income_tax_table 
                            WHERE valid_from <= %s
                            AND (valid_to IS NULL OR valid_to >= %s)
                            AND dependents_count = %s
                            AND monthly_income_from <= %s
                            AND monthly_income_to = %s
                            ORDER BY valid_from DESC
                            LIMIT 1
                            """,
                            (previous_date, previous_date, dependents_count, monthly_income, monthly_income)
                        )
                        result = cur.fetchone()
                        if result:
                            print(f"[DEBUG] 前年度で見つかった（To同値）: {previous_date}, 税額={result['tax_amount']}")
                            break
                
                if not result:
                    print(f"[DEBUG] 該当する所得税データなし、デフォルト値 0 を返す")
                
                return Decimal(str(result["tax_amount"])) if result else Decimal("0")
    
    @staticmethod
    def calculate_payroll(
        employee_id: int,
        payroll_year: int,
        payroll_month: int,
        working_days: Decimal,
        working_hours: Decimal,
        overtime_hours: Decimal,
        holiday_hours: Decimal,
        late_hours: Decimal,
        absent_days: Decimal,
        payment_date: date,
        other_allowance: Decimal = Decimal("0"),
        resident_tax: Decimal = Decimal("0"),
        other_deduction: Decimal = Decimal("0"),
        calculated_by: str = "system",
        calc_income_tax: bool = True,
        calc_social_insurance: bool = True
    ) -> Dict[str, Any]:
        """給与を計算"""
        
        # バリデーションエラーを集約
        errors = []
        
        # 給与設定を取得
        salary_setting = PayrollCalculationService.get_salary_setting(employee_id, payment_date)
        if not salary_setting:
            errors.append("給与設定が見つかりません")
        
        # 基本給の計算
        base_salary = Decimal(str(salary_setting["base_salary"])) if salary_setting else Decimal("0")
        
        # 時給の場合は勤務時間から計算
        if salary_setting and salary_setting["payment_type"] == "時給" and salary_setting["hourly_rate"]:
            hourly_rate = Decimal(str(salary_setting["hourly_rate"]))
            base_salary = hourly_rate * working_hours
        
        # 欠勤控除（日給制の場合）
        absence_deduction = Decimal("0")
        if absent_days > 0 and salary_setting and salary_setting["payment_type"] == "月給":
            # 月給を営業日数で割って欠勤日数を掛ける（簡易計算）
            daily_rate = base_salary / Decimal("22")  # 月平均営業日数を22日と仮定
            absence_deduction = daily_rate * absent_days
            base_salary = base_salary - absence_deduction
        
        # 残業手当の計算（時給 × 1.25）
        overtime_pay = Decimal("0")
        if overtime_hours > 0:
            if salary_setting and salary_setting["hourly_rate"]:
                hourly_rate = Decimal(str(salary_setting["hourly_rate"]))
            else:
                # 月給を時給に換算（月平均労働時間を160時間と仮定）
                hourly_rate = (Decimal(str(salary_setting["base_salary"])) if salary_setting else Decimal("0")) / Decimal("160")
            overtime_pay = hourly_rate * overtime_hours * Decimal("1.25")
        
        # 休日手当の計算（時給 × 1.35）
        holiday_pay = Decimal("0")
        if holiday_hours > 0:
            if salary_setting["hourly_rate"]:
                hourly_rate = Decimal(str(salary_setting["hourly_rate"]))
            else:
                hourly_rate = Decimal(str(salary_setting["base_salary"])) / Decimal("160")
            holiday_pay = hourly_rate * holiday_hours * Decimal("1.35")
        
        # 通勤手当
        commute_allowance = Decimal(str(salary_setting["commute_allowance"]))
        
        # 総支給額
        total_payment = (
            base_salary + overtime_pay + holiday_pay + 
            commute_allowance + other_allowance
        )
        
        # 標準報酬月額表から標準報酬月額を検索
        standard_monthly_salary = total_payment
        with get_connection() as conn:
            with conn.cursor() as cur:
                # 給与年月の標準報酬月額表から、総支給額が属する等級を検索
                target_year = payment_date.year
                result = None
                
                # まず指定年度の標準報酬月額表を検索
                cur.execute(
                    """
                    SELECT monthly_amount 
                    FROM standard_remuneration 
                    WHERE rate_year = %s
                    AND salary_from <= %s
                    AND (salary_to IS NULL OR salary_to > %s)
                    ORDER BY rate_year DESC, salary_from DESC
                    LIMIT 1
                    """,
                    (target_year, total_payment, total_payment)
                )
                result = cur.fetchone()
                
                # 見つからない場合は前年度以前を検索（最大3年遡る）
                if not result:
                    for year_offset in range(1, 4):
                        previous_year = target_year - year_offset
                        cur.execute(
                            """
                            SELECT monthly_amount 
                            FROM standard_remuneration 
                            WHERE rate_year = %s
                            AND salary_from <= %s
                            AND (salary_to IS NULL OR salary_to > %s)
                            ORDER BY rate_year DESC, salary_from DESC
                            LIMIT 1
                            """,
                            (previous_year, total_payment, total_payment)
                        )
                        result = cur.fetchone()
                        if result:
                            break
                
                if result:
                    standard_monthly_salary = Decimal(str(result["monthly_amount"]))
                    print(f"[DEBUG] 標準報酬月額表検索: 総支給額={total_payment} → 標準報酬月額={standard_monthly_salary}")
                else:
                    print(f"[DEBUG] 標準報酬月額表検索: 該当等級なし（総支給額={total_payment}）")

        
        # 社会保険料上限を取得
        health_insurance_limit = PayrollCalculationService.get_insurance_limit(
            "健康保険標準報酬月額上限", payment_date
        )
        pension_insurance_limit = PayrollCalculationService.get_insurance_limit(
            "厚生年金標準報酬月額上限", payment_date
        )
        commute_tax_free_limit = PayrollCalculationService.get_insurance_limit(
            "通勤手当非課税限度額", payment_date
        )
        
        # 標準報酬月額に上限を適用
        health_standard_salary = min(standard_monthly_salary, health_insurance_limit) if health_insurance_limit > 0 else standard_monthly_salary
        pension_standard_salary = min(standard_monthly_salary, pension_insurance_limit) if pension_insurance_limit > 0 else standard_monthly_salary
        
        # 従業員情報を取得（年齢計算のため）
        with get_connection() as conn:
            with conn.cursor() as cur:
                cur.execute(
                    "SELECT birth_date FROM employees WHERE employee_id = %s",
                    (employee_id,)
                )
                employee_info = cur.fetchone()
        
        # 年齢を計算（介護保険は40歳以上65歳未満が対象）
        age = 0
        if employee_info and employee_info["birth_date"]:
            birth_date = employee_info["birth_date"]
            # より正確な年齢計算
            age = payment_date.year - birth_date.year
            # 誕生日がまだ来ていない場合は-1
            if (payment_date.month, payment_date.day) < (birth_date.month, birth_date.day):
                age -= 1
        
        # 健康保険（標準報酬月額表から検索した月額で計算）
        health_insurance_rate = PayrollCalculationService.get_insurance_rate("健康保険", payment_date)
        health_insurance = Decimal("0")
        if health_insurance_rate:
            health_insurance = (
                health_standard_salary * Decimal(str(health_insurance_rate["employee_rate"]))
            ).quantize(Decimal("0.01"))
        elif calc_social_insurance:
            errors.append(f"健康保険料率が見つかりません（年度: {payment_date.year}）")
        
        # 介護保険（40歳以上65歳未満が対象、標準報酬月額表から検索した月額で計算）
        care_insurance = Decimal("0")
        if 40 <= age < 65:
            care_insurance_rate = PayrollCalculationService.get_insurance_rate("介護保険", payment_date)
            if care_insurance_rate:
                care_insurance = (
                    health_standard_salary * Decimal(str(care_insurance_rate["employee_rate"]))
                ).quantize(Decimal("0.01"))
                print(f"[DEBUG] 介護保険計算: {health_standard_salary} × {care_insurance_rate['employee_rate']}% = {care_insurance}")
            else:
                errors.append(f"介護保険料率が見つかりません（年度: {payment_date.year}）") if calc_social_insurance else None
        
        # 厚生年金（上限適用後の標準報酬月額で計算）
        pension_insurance_rate = PayrollCalculationService.get_insurance_rate("厚生年金", payment_date)
        pension_insurance = Decimal("0")
        if pension_insurance_rate:
            pension_insurance = (
                pension_standard_salary * Decimal(str(pension_insurance_rate["employee_rate"]))
            ).quantize(Decimal("0.01"))
            print(f"[DEBUG] 厚生年金計算: {pension_standard_salary} × {pension_insurance_rate['employee_rate']}% = {pension_insurance}")
        elif calc_social_insurance:
            errors.append(f"厚生年金保険料率が見つかりません（年度: {payment_date.year}）")
        
        # 雇用保険
        employment_insurance_rate = PayrollCalculationService.get_insurance_rate("雇用保険", payment_date)
        employment_insurance = Decimal("0")
        
        # 雇用保険加入対象かどうか確認
        is_employment_insurance_eligible = salary_setting.get("employment_insurance_eligible", True) if salary_setting else True
        
        if is_employment_insurance_eligible and employment_insurance_rate:
            employment_insurance = (
                total_payment * Decimal(str(employment_insurance_rate["employee_rate"]))
            ).quantize(Decimal("0.01"))
        elif not is_employment_insurance_eligible:
            print(f"[DEBUG] この従業員は雇用保険非対象です")
        elif not employment_insurance_rate and calc_social_insurance:
            errors.append(f"雇用保険料率が見つかりません（年度: {payment_date.year}）")
        
        # 社会保険計算スキップ時は全て。0にする
        if not calc_social_insurance:
            health_insurance = Decimal("0")
            care_insurance = Decimal("0")
            pension_insurance = Decimal("0")
            employment_insurance = Decimal("0")
        
        # エラーがある場合は、エラーメッセージを返す
        if errors:
            raise ValueError(f"給与計算に必要なデータが不足しています:\n" + "\n".join(errors))
        
        # 所得税の計算
        dependents_count = PayrollCalculationService.get_active_dependents_count(employee_id, payment_date)
        
        # 通勤手当非課税限度額を取得（設定がなければ150000円）
        if commute_tax_free_limit == Decimal("0"):
            commute_tax_free_limit = Decimal("150000")
        
        # 課税対象額 = 総支給額 - 通勤手当（非課税限度額まで） - 社会保険料
        taxable_income = (
            total_payment - 
            min(commute_allowance, commute_tax_free_limit) - 
            health_insurance - 
            care_insurance -
            pension_insurance - 
            employment_insurance
        )
        
        if calc_income_tax:
            income_tax = PayrollCalculationService.calculate_income_tax(
                taxable_income, 
                dependents_count, 
                payment_date
            )
        else:
            income_tax = Decimal("0")
        
        # 総控除額
        total_deduction = (
            health_insurance + 
            care_insurance +
            pension_insurance + 
            employment_insurance + 
            income_tax + 
            resident_tax + 
            other_deduction
        )
        
        # 差引支給額
        net_payment = total_payment - total_deduction
        
        return {
            "employee_id": employee_id,
            "payroll_year": payroll_year,
            "payroll_month": payroll_month,
            "payment_date": payment_date,
            "status": "calculated",
            
            # 勤怠情報
            "working_days": working_days,
            "working_hours": working_hours,
            "overtime_hours": overtime_hours,
            "holiday_hours": holiday_hours,
            "late_hours": late_hours,
            "absent_days": absent_days,
            
            # 支給項目
            "base_salary": base_salary,
            "overtime_pay": overtime_pay,
            "holiday_pay": holiday_pay,
            "commute_allowance": commute_allowance,
            "other_allowance": other_allowance,
            "total_payment": total_payment,
            
            # 控除項目
            "health_insurance": health_insurance,
            "care_insurance": care_insurance,
            "pension_insurance": pension_insurance,
            "employment_insurance": employment_insurance,
            "income_tax": income_tax,
            "resident_tax": resident_tax,
            "other_deduction": other_deduction,
            "total_deduction": total_deduction,
            
            # 差引支給額
            "net_payment": net_payment,
            
            "calculated_by": calculated_by
        }
