<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Mail\EmailOtpMail;
use App\Services\EmailOtpService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class LoginController extends Controller
{
    /**
     * ログイン成功後のリダイレクト先
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * コントローラーのインスタンス作成
     * Laravel 12変更点：ミドルウェアは routes/web.php で処理するように変更
     *
     * @return void
     */
    public function __construct()
    {
        // Laravel 12: ミドルウェアは routes/web.php で処理
        // Laravel 5.7: $this->middleware('guest')->except('logout'); を使用していた
    }

    /**
     * ログインフォームを表示
     *
     * @return \Illuminate\View\View
     */
    public function showLoginForm()
    {
        return view('auth.login');
    }

    /**
     * ログインリクエストを処理
     * Laravel 12変更点：AuthenticatesUsersトレイトを使わず独自実装
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
     */
    public function login(Request $request)
    {
        $this->validateLogin($request);

        // ログイン試行回数制限チェック
        if ($this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);
            return $this->sendLockoutResponse($request);
        }

        // ログイン認証試行
        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }

        // ログイン失敗時の処理
        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }

    /**
     * ログインリクエストのバリデーション
     *
     * 仕様上の入力名（フォーム側）は ope_id / ope_pass のまま維持し、
     * 内部の認証キーは login_id に寄せる（login_id 統一）。
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    protected function validateLogin(Request $request)
    {
        // 個別未入力メッセージ（仕様1,2）
        $request->validate([
            'ope_id'   => 'required|string', // フォームの入力名は現状維持（実体は login_id）
            'ope_pass' => 'required|string',
        ], [
            'ope_id.required'   => 'ログインIDが未入力です。',
            'ope_pass.required' => 'パスワードが未入力です。',
        ]);
    }

    /**
     * ログイン認証を試行
     *
     * 
     * - 画面入力（ope_id）= DBの login_id として扱う
     * - 退職フラグチェックも login_id で取得して判定する
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function attemptLogin(Request $request)
    {
        // 先にIDのみでオペレータ取得して退職フラグを確認（仕様5-1）
        $loginId = $request->input('ope_id'); // 入力名は ope_id だが中身は login_id
        $operator = \App\Models\Ope::where('login_id', $loginId)->first();

        if ($operator && (int)($operator->ope_quit_flag) === 1) {
            // 退職扱いは認証失敗と同じメッセージ（仕様5-1 と 3/4 統一表示）
            return false;
        }

        // 認証実行（credentials() で login_id / password を渡す）
        return Auth::attempt($this->credentials($request), false);
    }

    /**
     * 認証用の資格情報を取得
     *
     * 
     * - 認証IDを login_id に統一
     * - パスワード入力（ope_pass）は Auth 側の password にマッピング
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    protected function credentials(Request $request)
    {
        return [
            'login_id' => $request->input('ope_id'),   // フォーム入力（ope_id）→ DB列 login_id
            'password' => $request->input('ope_pass'), // フォーム入力（ope_pass）→ 認証用 password
        ];
    }

    /**
     * ログイン成功時のレスポンス
     *
     * OTP認証チェック：
     * - 24時間以内に OTP 認証済みの場合：/home にリダイレクト
     * - 未認証の場合：OTP メール送信 → /otp にリダイレクト
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
     */
    protected function sendLoginResponse(Request $request)
    {
        $request->session()->regenerate();
        $this->clearLoginAttempts($request);

        // 仕様5: ログインIDをセッション保持
        // ここで保持する値も login_id（入力名は ope_id のまま）
        $request->session()->put('login_ope_id', $request->input('ope_id'));

        // OTP認証チェック
        $otpService = app(EmailOtpService::class);
        $user = Auth::user();

        // 24時間以内に OTP 認証済みの場合
        if ($otpService->isOtpRecent($user)) {
            return redirect()->intended($this->redirectTo);
        }

        // OTP 未認証の場合：OTP コード発行 → メール送信 → /otp にリダイレクト
        try {
            $otpCode = $otpService->issue($user);

            // ope_mail はセミコロン区切りで複数アドレスを保持する可能性があるため、最初のアドレスのみ抽出
            $operatorEmails = explode(';', trim($user->ope_mail));
            $primaryEmail = trim($operatorEmails[0] ?? $user->ope_mail);

            Log::info('OTP メール送信開始: ' . $primaryEmail);
            
            Mail::to($primaryEmail)->send(new EmailOtpMail(
                $otpCode,
                $user->name ?? 'ユーザー'
            ));

            Log::info('OTP メール送信完了: ' . $primaryEmail);
            
            return redirect()->route('otp.show')
                ->with('info', 'OTP認証コードをメール送信しました。');
        } catch (\Exception $e) {
            Log::error('OTP issue/send failed: ' . $e->getMessage(), [
                'exception' => $e,
                'user_id' => $user->ope_id ?? null,
                'user_email' => $user->ope_mail ?? null,
            ]);
            
            // メール送信エラー時は home にリダイレクトするか、カスタムエラーを返す
            return redirect($this->redirectTo)
                ->with('warning', 'OTP認証メールの送信に失敗しました。');
        }
    }

    /**
     * ログイン失敗時のレスポンス
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse
     */
    protected function sendFailedLoginResponse(Request $request)
    {
        // 画面側のエラー表示キーは仕様に合わせて ope_id のまま
        throw ValidationException::withMessages([
            'ope_id' => [trans('auth.failed')],
        ]);
    }

    /**
     * ログアウト処理
     * Laravel 12変更点：セッション無効化処理を追加
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
     */
    public function logout(Request $request)
    {
        Auth::logout();

        // Laravel 12: セッション無効化とトークン再生成を明示的に実行
        $request->session()->invalidate();
        $request->session()->regenerateToken();

        return redirect('/login');
    }

    /**
     * ログイン試行回数が上限を超えているかチェック
     * Laravel 12変更点：RateLimiterファサードを使用
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function hasTooManyLoginAttempts(Request $request)
    {
        return RateLimiter::tooManyAttempts(
            $this->throttleKey($request),
            5
        );
    }

    /**
     * ログイン試行回数をインクリメント
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    protected function incrementLoginAttempts(Request $request)
    {
        RateLimiter::hit(
            $this->throttleKey($request),
            60
        );
    }

    /**
     * ログイン試行回数制限をクリア
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    protected function clearLoginAttempts(Request $request)
    {
        RateLimiter::clear($this->throttleKey($request));
    }

    /**
     * ロックアウト発生時のイベント処理
     *
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    protected function fireLockoutEvent(Request $request)
    {
        // 必要に応じてイベントを発火
    }

    /**
     * レート制限用のスロットルキーを取得
     *
     * 
     * - 画面入力名は ope_id のまま
     * - ただし内容は login_id を想定（ログインID文字列）
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string
     */
    protected function throttleKey(Request $request)
    {
        return Str::lower($request->input('ope_id')) . '|' . $request->ip();
    }

    /**
     * ロックアウト時のレスポンス
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse
     */
    protected function sendLockoutResponse(Request $request)
    {
        $seconds = RateLimiter::availableIn(
            $this->throttleKey($request)
        );

        // 画面側のエラー表示キーは仕様に合わせて ope_id のまま
        throw ValidationException::withMessages([
            'ope_id' => [trans('auth.throttle', ['seconds' => $seconds])],
        ]);
    }
}
