<?php

namespace App\Http\Controllers\API\v1\Admin\Allowance;

use App\AllowanceClaimCode;
use App\AllowancePayout;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Util;
use App\Result;
use App\Voter;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Hash;

class PayoutController extends Controller
{
    public function __invoke(Request $request)
    {
        $this->validate($request, [
            'voter_id' => 'required|integer',
            'method' => 'nullable|string|in:cash,bank',
            'amount_cents' => 'nullable|integer|min:0',
            'reference' => 'nullable|string|max:80',
            'note' => 'nullable|string|max:2000',
            'claim_code' => 'nullable|string|min:4|max:20',
        ]);

        $electionId = Util::getCurrentElection();
        $method = $request->input('method', 'cash');
        $voterId = (int) $request->input('voter_id');

        $voter = Voter::where('election_id', $electionId)->where('id', $voterId)->first();
        if (!$voter) {
            return response()->json([
                'status' => 'failed',
                'message' => 'Voter not found for current election.',
            ], 404);
        }

        // Enforce method by attendance type (paperless replacement for coupon rules)
        if ($method === 'cash' && (int) $voter->kehadiran !== 1) {
            return response()->json([
                'status' => 'failed',
                'message' => 'Cash payout only allowed for Fizikal attendance.',
            ], 422);
        }
        if ($method === 'bank' && (int) $voter->kehadiran !== 2) {
            return response()->json([
                'status' => 'failed',
                'message' => 'Bank payout only allowed for Maya attendance.',
            ], 422);
        }

        $hasVoted = Result::where('election_id', $electionId)->where('voter_id', $voterId)->exists();

        // Tunai: normally must have voted; exception — fizikal hadir & layak mesyuarat tetapi tidak dibenarkan
        // mengundi kerana saham < RM500 (same rule as claim code).
        $kehadiran = (int) $voter->kehadiran;
        $saham = (float) $voter->saham;
        $canVoteBySaham = $saham >= 500.0;
        $canProceedToVote = $kehadiran !== 0
            && ($kehadiran !== 1 || $voter->fizikal_registration_verified_at !== null);
        $eligibleCashPayoutWithoutVote = $method === 'cash'
            && $kehadiran === 1
            && $canProceedToVote
            && !$canVoteBySaham;

        if (!$hasVoted && !$eligibleCashPayoutWithoutVote) {
            return response()->json([
                'status' => 'failed',
                'message' => 'Voter has not voted yet.',
            ], 422);
        }

        // Default amounts (can be overridden by passing amount_cents)
        $defaultAmount = $method === 'cash' ? 30000 : 15000;
        $amountCents = $request->filled('amount_cents') ? (int) $request->input('amount_cents') : $defaultAmount;

        $now = Carbon::now();
        $adminId = $request->user() ? (int) $request->user()->id : null;

        $claimCode = $request->input('claim_code');

        // For fizikal cash payout, require claim code (paperless coupon).
        if ($method === 'cash' && (!$claimCode || trim($claimCode) === '')) {
            return response()->json([
                'status' => 'failed',
                'message' => 'Sila masukkan kod tuntutan (claim code) sebelum bayaran tunai dibuat.',
            ], 422);
        }

        $payout = DB::transaction(function () use ($electionId, $voterId, $method, $claimCode, $amountCents, $adminId, $now, $request) {
            // Consume claim code if provided
            if ($claimCode && trim($claimCode) !== '') {
                $row = AllowanceClaimCode::where('election_id', $electionId)
                    ->where('voter_id', $voterId)
                    ->where('method', $method)
                    ->lockForUpdate()
                    ->first();

                if (!$row) {
                    return response()->json([
                        'status' => 'failed',
                        'message' => 'Kod tuntutan tidak dijumpai.',
                    ], 404);
                }

                if ($row->used_at) {
                    return response()->json([
                        'status' => 'failed',
                        'message' => 'Kod tuntutan telah digunakan.',
                    ], 422);
                }

                if ($row->expires_at && Carbon::now()->greaterThan($row->expires_at)) {
                    return response()->json([
                        'status' => 'failed',
                        'message' => 'Kod tuntutan telah tamat tempoh.',
                    ], 422);
                }

                if (!Hash::check(trim((string) $claimCode), $row->code_hash)) {
                    return response()->json([
                        'status' => 'failed',
                        'message' => 'Kod tuntutan tidak sah.',
                    ], 422);
                }

                $row->used_at = Carbon::now();
                $row->used_by_admin_id = $adminId;
                $row->save();
            }

            // Idempotent: if already exists (unique election_id+voter_id+method) we keep it as paid.
            return AllowancePayout::updateOrCreate(
                [
                    'election_id' => $electionId,
                    'voter_id' => $voterId,
                    'method' => $method,
                ],
                [
                    'status' => 'paid',
                    'amount_cents' => $amountCents,
                    'currency' => 'MYR',
                    'paid_by_admin_id' => $adminId,
                    'paid_at' => $now,
                    'reference' => $request->input('reference'),
                    'note' => $request->input('note'),
                ]
            );
        });

        // In case the transaction returned a JSON response (validation failure), return it directly.
        if ($payout instanceof \Illuminate\Http\JsonResponse) {
            return $payout;
        }

        activity()
            ->performedOn($payout)
            ->withProperties([
                'election_id' => $electionId,
                'voter_id' => $voterId,
                'voter_name' => $voter->name,
                'attendance' => (int) $voter->kehadiran,
                'method' => $method,
                'amount_cents' => (int) $payout->amount_cents,
                'currency' => $payout->currency,
                'paid_by_admin_id' => $adminId,
                'reference' => $payout->reference,
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent(),
            ])
            ->log("allowance payout ({$method}): {$voter->name}");

        return response()->json([
            'status' => 'success',
            'message' => 'Allowance payout recorded.',
            'payout' => $payout,
            'paid_by_admin_name' => $request->user() ? $request->user()->name : null,
            'paid_by_admin_email' => $request->user() ? $request->user()->email : null,
        ]);
    }
}

