<?php
// app/Validation/CustomRules.php

namespace App\Validation;

use Config\Services;
use App\Models\ParticipantModel;

class CustomRules
{

    public function validate_kta_and_birthdate(?string $ktaNumber, string $fields, array $data, ?string &$error = null): bool
    {
        if (empty($ktaNumber)) {
            return true;
        }

        // Fungsi dot_array_search tidak standar di CI4, pastikan ada di helper Anda
        $birthDateFromInput = $this->dot_array_search($fields, $data);
        
        if (empty($birthDateFromInput)) {
            $error = lang('Validation.kta_birth_date_required');
            return false;
        }

        try {
            $client = Services::curlrequest(['timeout' => 10, 'verify' => false]);
            $requestBody = http_build_query([
                'username' => getenv('api.gbika.username'),
                'password' => getenv('api.gbika.password'),
                'nokta'    => $ktaNumber,
            ]);
            
            $response = $client->request('POST', getenv('api.gbika.url'), [
                'body' => $requestBody,
                'headers' => ['Content-Type' => 'application/x-www-form-urlencoded']
            ]);

            $apiData = json_decode($response->getBody(), true);

            // Skenario 1: Kredensial API salah atau API error
            if (isset($apiData['status']) && ($apiData['status'] === false || $apiData['status'] === 401)) {
                 $error = lang('Validation.kta_api_unavailable');
                 log_message('critical', 'API GBIKA credential invalid or service error.');
                 return false;
            }

            // Skenario 2: KTA tidak ditemukan
            if (empty($apiData['data'])) {
                $error = lang('Validation.kta_not_found', ['value' => esc($ktaNumber)]);
                return false;
            }

            // Skenario 3: KTA ditemukan, tapi tanggal lahir tidak cocok
            $birthDateFromApi = $apiData['data']['tgl_lahir'] ?? null;
            if (!$birthDateFromApi || date('Y-m-d', strtotime($birthDateFromApi)) !== $birthDateFromInput) {
                $error = lang('Validation.kta_birth_date_mismatch', ['value' => esc($ktaNumber)]);
                return false;
            }

            return true;

        } catch (\Exception $e) {
            log_message('error', 'Error calling GBIKA API: ' . $e->getMessage());
            $error = lang('Validation.kta_api_error');
            return false;
        }
    }


    public function is_unique_participant_kta(?string $ktaNumber, string $fields, array $data, ?string &$error = null): bool
    {
        if (empty($ktaNumber)) {
            return true;
        }

        $eventId = $this->dot_array_search($fields, $data);
        if (!$eventId) {
            return false;
        }

        $participantModel = new \App\Models\ParticipantModel();
        
        $activeStatuses = ['pending', 'paid'];
        $existing = $participantModel->select('participants.id')
            ->join('transactions', 'transactions.id = participants.transaction_id')
            ->where('transactions.event_id', $eventId)
            ->where('participants.kta_number', $ktaNumber)
            ->whereIn('transactions.payment_status', $activeStatuses)
            ->first();
            
        if ($existing) {
            $error = lang('Validation.kta_already_registered', ['value' => esc($ktaNumber)]);
            return false;
        }

        return true;
    }

    public function is_unique_participant_kta_except(?string $ktaNumber, string $fields, array $data, ?string &$error = null): bool
    {
        if (empty($ktaNumber)) {
            return true;
        }
        
        
        [$eventId, $participantIdToExclude] = explode(',', $fields);
        
        if (!is_numeric($eventId) || !is_numeric($participantIdToExclude)) {
            return false;
        }

        $participantModel = new \App\Models\ParticipantModel();
        $query = $participantModel->select('participants.id')
            ->join('transactions', 'transactions.id = participants.transaction_id')
            ->where('transactions.event_id', $eventId) // Langsung gunakan eventId dari parameter
            ->where('participants.kta_number', $ktaNumber)
            ->where('participants.id !=', $participantIdToExclude);
            
        $activeStatuses = ['pending', 'paid'];
        $query->whereIn('transactions.payment_status', $activeStatuses);

        if ($query->first()) {
            $error = lang('Validation.kta_already_registered', ['value' => esc($ktaNumber)]);
            return false;
        }

        return true;
    }

    public function has_no_duplicate_kta(?array $participants, ?string $fields = null, array $data = [], ?string &$error = null): bool
    {
        if (empty($participants)) {
            return true;
        }

        $ktaNumbers = array_column(array_filter($participants, fn($p) => !empty($p['kta_number'])), 'kta_number');
        
        if (empty($ktaNumbers)) {
            return true;
        }

        $counts = array_count_values($ktaNumbers);
        
        foreach ($counts as $kta => $count) {
            if ($count > 1) {
                $error = lang('Validation.kta_duplicate_in_request', ['value' => esc($kta)]);
                return false;
            }
        }

        return true;
    }

    public function is_unique_user_kta_except(?string $ktaNumber, string $fields, array $data, ?string &$error = null): bool
    {
        if (empty($ktaNumber)) {
            return true;
        }
        
        $userIdToExclude = $fields;
        
        $userModel = new \App\Models\UserModel();

        $existingUser = $userModel
            ->where('kta_number', $ktaNumber)
            ->where('id !=', $userIdToExclude)
            ->where('deleted_at', null) 
            ->first();

        if ($existingUser) {
            $error = lang('Validation.kta_in_use_by_other_user');
            return false;
        }

        return true;
    }

    private function dot_array_search(string $index, array $array)
    {
        $keys = explode('.', $index);
        foreach ($keys as $key) {
            if (!is_array($array) || !array_key_exists($key, $array)) {
                return null;
            }
            $array = $array[$key];
        }
        return $array;
    }

    public function date_range_within(?string $value, string $params, array $data, ?string &$error = null): bool
    {
        if (empty($value)) return true;

        [$rangeStartStr, $rangeEndStr] = explode(',', $params);

        $dateToCheck = new \DateTime($value);
        $startDate = new \DateTime($rangeStartStr);
        $startDate->setTime(0, 0, 0); 
        
        $endDate = new \DateTime($rangeEndStr);
        $endDate->setTime(23, 59, 59);

        if ($dateToCheck < $startDate || $dateToCheck > $endDate) {
            $error = "The date must be between " . $startDate->format('d M Y') . " and " . $endDate->format('d M Y') . ".";
            return false;
        }

        return true;
    }

    public function after_or_equal_to_field(?string $value, string $field, array $data, ?string &$error = null): bool
    {
        if (empty($value) || empty($data[$field])) return true;

        if (strtotime($value) < strtotime($data[$field])) {
            $error = 'The {field} must be a date after or equal to the start date.';
            return false;
        }
        return true;
    }
}