<?php

declare(strict_types=1);

namespace App\Controller\External;

use App\Core\Database;
use App\Core\Response;

/**
 * 外部API用 勤務集計コントローラー
 * APIキー認証により外部ツール（MCPサーバー等）からシフトデータを取得できる
 * GET /api/external/work-summary/{year}/{month}
 */
class WorkSummaryController
{
    /**
     * 月別勤務集計データを返す
     */
    public function index(array $params = []): void
    {
        $this->authenticate();

        $year  = (int)($params['year']  ?? date('Y'));
        $month = (int)($params['month'] ?? date('n'));

        if ($year < 2000 || $year > 2100 || $month < 1 || $month > 12) {
            Response::error('年月の指定が不正です。', 400);
        }

        $reiwaYear   = $year - 2018;
        $targetMonth = sprintf('%04d-%02d-01', $year, $month);
        $daysInMonth = (int)date('t', strtotime($targetMonth));
        $pdo         = Database::getInstance();

        // 施設情報を取得する
        $facilityStmt = $pdo->query(
            "SELECT facility_name, full_time_weekly_hours, office_number
             FROM facility_settings WHERE id = 1"
        );
        $facility = $facilityStmt->fetch() ?: [
            'facility_name'          => '',
            'full_time_weekly_hours' => 32.0,
            'office_number'          => '',
        ];

        // 当月全シフトを取得する
        $shiftStmt = $pdo->prepare(
            "SELECT s.user_id,
                    s.shift_date,
                    s.shift_type,
                    TO_CHAR(s.start_time, 'HH24:MI') AS start_time,
                    TO_CHAR(s.end_time,   'HH24:MI') AS end_time,
                    s.work_minutes,
                    s.is_confirmed,
                    wp.short_name,
                    wp.pattern_code
             FROM shifts s
             LEFT JOIN work_patterns wp ON s.work_pattern_id = wp.id
             WHERE s.target_month = :target_month
             ORDER BY s.shift_date"
        );
        $shiftStmt->execute([':target_month' => $targetMonth]);
        $shiftRows = $shiftStmt->fetchAll();

        // [user_id][date] => shift の形式に変換する
        $shiftMap = [];
        foreach ($shiftRows as $row) {
            $shiftMap[$row['user_id']][$row['shift_date']] = $row;
        }

        // 在籍職員一覧を取得する（職種ソート順 → 氏名カナ順）
        $staffStmt = $pdo->prepare(
            "SELECT u.id, u.user_code, u.name,
                    u.weekly_contracted_hours,
                    et.name AS employment_type_name,
                    et.is_full_time,
                    jt.name     AS job_type_name,
                    jt.sort_order AS job_sort_order
             FROM users u
             LEFT JOIN employment_types et ON u.employment_type_id = et.id
             LEFT JOIN job_types jt        ON u.job_type_id = jt.id
             WHERE u.deleted_at IS NULL
               AND u.is_admin = FALSE
               AND (u.retirement_date IS NULL OR u.retirement_date >= :month_start)
             ORDER BY jt.sort_order, u.name_kana"
        );
        $staffStmt->execute([':month_start' => $targetMonth]);
        $staffRows = $staffStmt->fetchAll();

        // 集計基準日数（4週8休書式に合わせて1〜28日固定）
        $aggregateDays = 28;

        // 職員ごとに日別シフトデータを構築する
        $staffList = [];
        foreach ($staffRows as $staff) {
            $userId       = (int)$staff['id'];
            $daily        = [];
            $totalMinutes = 0;  // 集計は1〜28日のみ
            $workDays     = 0;  // 集計は1〜28日のみ

            for ($d = 1; $d <= $daysInMonth; $d++) {
                $dateStr = sprintf('%04d-%02d-%02d', $year, $month, $d);
                $shift   = $shiftMap[$userId][$dateStr] ?? null;

                if ($shift) {
                    $minutes   = (int)($shift['work_minutes'] ?? 0);
                    $workHours = $minutes > 0 ? round($minutes / 60, 2) : 0.0;
                    $shiftCode = $this->resolveShiftCode($shift);

                    // 集計（合計時間・出勤日数）は1〜28日のみ対象
                    if ($d <= $aggregateDays
                        && in_array($shift['shift_type'], ['work', 'late', 'early_leave'], true)) {
                        $totalMinutes += $minutes;
                        $workDays++;
                    }

                    $daily[$d] = [
                        'date'         => $dateStr,
                        'shift_type'   => $shift['shift_type'],
                        'start_time'   => $shift['start_time'],
                        'end_time'     => $shift['end_time'],
                        'work_hours'   => $workHours,
                        'shift_code'   => $shiftCode,
                        // 勤務パターンが紐付いている場合のみコードを返す（連携・確認用）
                        'pattern_code' => $shift['pattern_code'] ?? null,
                    ];
                }
            }

            // 常勤換算基準（週時間 × 4.333週 × 60分）
            $fullTimeWeeklyHours  = (float)$facility['full_time_weekly_hours'];
            $fullTimeMonthlyMins  = $fullTimeWeeklyHours * 4.333 * 60;
            $weeklyHours          = (float)($staff['weekly_contracted_hours'] ?? $fullTimeWeeklyHours);
            $totalHours           = round($totalMinutes / 60, 2);
            $weeklyAvgHours       = round($totalHours / 4, 2);
            $fteRatio             = $fullTimeMonthlyMins > 0
                ? round($totalMinutes / $fullTimeMonthlyMins, 4)
                : 0.0;

            $staffList[] = [
                'id'                   => $userId,
                'user_code'            => $staff['user_code'],
                'name'                 => $staff['name'],
                'job_type_name'        => $staff['job_type_name']        ?? '',
                'employment_type_name' => $staff['employment_type_name'] ?? '',
                'is_full_time'         => (bool)($staff['is_full_time']  ?? false),
                'weekly_hours'         => $weeklyHours,
                'work_days'            => $workDays,
                'total_work_hours'     => $totalHours,
                'weekly_avg_hours'     => $weeklyAvgHours,
                'fte_ratio'            => $fteRatio,
                'daily'                => $daily,
            ];
        }

        Response::json([
            'year'                   => $year,
            'month'                  => $month,
            'reiwa_year'             => $reiwaYear,
            'days_in_month'          => $daysInMonth,
            'aggregate_days'         => $aggregateDays,  // 集計基準日数（4週=28日固定）
            'facility_name'          => $facility['facility_name'],
            'office_number'          => $facility['office_number'] ?? '',
            'full_time_weekly_hours' => (float)$facility['full_time_weekly_hours'],
            'staff'                  => $staffList,
        ]);
    }

    /**
     * APIキー認証を行う
     * X-API-Key ヘッダーと .env の EXTERNAL_API_KEY を比較する
     */
    private function authenticate(): void
    {
        $provided = $_SERVER['HTTP_X_API_KEY'] ?? '';
        $expected = $_ENV['EXTERNAL_API_KEY'] ?? getenv('EXTERNAL_API_KEY') ?: '';

        if ($expected === '' || !hash_equals($expected, $provided)) {
            Response::error('APIキーが無効です。', 401);
        }
    }

    /**
     * シフト情報からExcelテンプレートの勤務形態コードを決定する
     * work_patterns.short_name が設定されている場合はそちらを優先する
     * 未設定時は開始・終了時刻の組み合わせでコードを決定する
     *
     * テンプレート凡例:
     *   a = 09:30-18:30, b = 08:30-17:30, c = 09:30-15:30
     *   d = 16:00-17:00, e = 休日
     */
    private function resolveShiftCode(array $shift): string
    {
        // 休日・欠勤・有休はすべて e（休日）
        if (in_array($shift['shift_type'], ['off', 'holiday', 'absence', 'paid_leave'], true)) {
            return 'e';
        }

        // work_patterns に short_name が設定されている場合は優先
        if (!empty($shift['short_name'])) {
            return $shift['short_name'];
        }

        // 開始・終了時刻でテンプレートコードを決定する
        $start = substr($shift['start_time'] ?? '', 0, 5);
        $end   = substr($shift['end_time']   ?? '', 0, 5);

        return match (true) {
            $start === '09:30' && $end === '18:30' => 'a',
            $start === '08:30' && $end === '17:30' => 'b',
            $start === '09:30' && $end === '15:30' => 'c',
            $start === '16:00' && $end === '17:00' => 'd',
            default                                 => 'a',
        };
    }
}
