<?php
declare(strict_types=1);

namespace App\Modules\Members\Services;

use App\Core\App;

final class NationalIdParser
{
    /**
     * Parse an Egyptian 14-digit National ID.
     *
     * Structure:
     *   Position 1:    Century (2 = 1900s, 3 = 2000s)
     *   Position 2-3:  Year of birth
     *   Position 4-5:  Month of birth
     *   Position 6-7:  Day of birth
     *   Position 8-9:  Governorate code
     *   Position 10-13: Sequential number
     *   Position 13:   Odd = male, Even = female
     *   Position 14:   Check digit (algorithm undocumented — NOT validated)
     */
    public static function parse(string $nid): array
    {
        $result = [
            'is_valid'            => false,
            'national_id'         => $nid,
            'dob'                 => null,
            'age_years'           => null,
            'age_months'          => null,
            'gender'              => null,
            'governorate_code'    => null,
            'governorate_name_ar' => null,
            'governorate_name_en' => null,
            'century'             => null,
            'errors'              => [],
        ];

        // Must be exactly 14 digits
        if (strlen($nid) !== 14) {
            $result['errors'][] = 'الرقم القومي يجب أن يكون 14 رقم';
            return $result;
        }

        if (!ctype_digit($nid)) {
            $result['errors'][] = 'الرقم القومي يجب أن يحتوي على أرقام فقط';
            return $result;
        }

        // Extract components
        $centuryCode = (int) $nid[0];
        $yearPart    = substr($nid, 1, 2);
        $monthPart   = substr($nid, 3, 2);
        $dayPart     = substr($nid, 5, 2);
        $govCode     = substr($nid, 7, 2);
        $seqNumber   = substr($nid, 9, 4);
        $genderDigit = (int) $nid[12]; // Position 13 (0-indexed: 12)

        // Validate century
        if (!in_array($centuryCode, [2, 3])) {
            $result['errors'][] = 'رمز القرن غير صالح (يجب أن يكون 2 أو 3)';
            return $result;
        }

        $century = ($centuryCode === 2) ? 1900 : 2000;
        $result['century'] = $century;

        // Calculate full year
        $year = $century + (int) $yearPart;

        // Validate month
        $month = (int) $monthPart;
        if ($month < 1 || $month > 12) {
            $result['errors'][] = 'الشهر غير صالح: ' . $monthPart;
            return $result;
        }

        // Validate day
        $day = (int) $dayPart;
        if ($day < 1 || $day > 31) {
            $result['errors'][] = 'اليوم غير صالح: ' . $dayPart;
            return $result;
        }

        // Validate the full date exists
        if (!checkdate($month, $day, $year)) {
            $result['errors'][] = "التاريخ غير صالح: {$year}-{$monthPart}-{$dayPart}";
            return $result;
        }

        // Date of birth
        $dob = sprintf('%04d-%02d-%02d', $year, $month, $day);

        // Check date is not in the future
        $dobTimestamp = strtotime($dob);
        if ($dobTimestamp === false || $dobTimestamp > time()) {
            $result['errors'][] = 'تاريخ الميلاد في المستقبل';
            return $result;
        }

        // Check reasonable age (0-120 years)
        $age = self::calculateAge($dob);
        if ($age['years'] > 120) {
            $result['errors'][] = 'العمر غير منطقي (أكثر من 120 سنة)';
            return $result;
        }

        // Validate governorate code
        $govData = self::getGovernorate($govCode);
        if ($govData === null) {
            $result['errors'][] = 'كود المحافظة غير صالح: ' . $govCode;
            return $result;
        }

        // Determine gender (position 13: odd = male, even = female)
        $gender = ($genderDigit % 2 === 1) ? 'male' : 'female';

        // All valid
        $result['is_valid']            = true;
        $result['dob']                 = $dob;
        $result['age_years']           = $age['years'];
        $result['age_months']          = $age['months'];
        $result['gender']              = $gender;
        $result['governorate_code']    = $govCode;
        $result['governorate_name_ar'] = $govData['name_ar'];
        $result['governorate_name_en'] = $govData['name_en'];
        $result['errors']              = [];

        return $result;
    }

    /**
     * Calculate age from date of birth.
     */
    private static function calculateAge(string $dob): array
    {
        try {
            $birth = new \DateTime($dob);
            $now = new \DateTime();
            $diff = $now->diff($birth);
            return [
                'years'  => $diff->y,
                'months' => $diff->m,
                'days'   => $diff->d,
            ];
        } catch (\Throwable $e) {
            return ['years' => 0, 'months' => 0, 'days' => 0];
        }
    }

    /**
     * Look up governorate by code.
     * First checks the database, then falls back to hardcoded list.
     */
    private static function getGovernorate(string $code): ?array
    {
        // Try database first
        try {
            $db = App::getInstance()->db();
            $row = $db->selectOne(
                "SELECT name_ar, name_en FROM governorates WHERE code = ? AND is_active = 1",
                [$code]
            );
            if ($row) {
                return $row;
            }
        } catch (\Throwable $e) {
            // Fall through to hardcoded list
        }

        // Hardcoded fallback — all Egyptian governorates
        $governorates = [
            '01' => ['name_ar' => 'القاهرة',              'name_en' => 'Cairo'],
            '02' => ['name_ar' => 'الإسكندرية',           'name_en' => 'Alexandria'],
            '03' => ['name_ar' => 'بورسعيد',              'name_en' => 'Port Said'],
            '04' => ['name_ar' => 'السويس',               'name_en' => 'Suez'],
            '11' => ['name_ar' => 'دمياط',                'name_en' => 'Damietta'],
            '12' => ['name_ar' => 'الدقهلية',             'name_en' => 'Dakahlia'],
            '13' => ['name_ar' => 'الشرقية',              'name_en' => 'Sharqia'],
            '14' => ['name_ar' => 'القليوبية',            'name_en' => 'Qalyubia'],
            '15' => ['name_ar' => 'كفر الشيخ',            'name_en' => 'Kafr El Sheikh'],
            '16' => ['name_ar' => 'الغربية',              'name_en' => 'Gharbia'],
            '17' => ['name_ar' => 'المنوفية',             'name_en' => 'Menoufia'],
            '18' => ['name_ar' => 'البحيرة',              'name_en' => 'Beheira'],
            '19' => ['name_ar' => 'الإسماعيلية',          'name_en' => 'Ismailia'],
            '21' => ['name_ar' => 'الجيزة',               'name_en' => 'Giza'],
            '22' => ['name_ar' => 'بني سويف',             'name_en' => 'Beni Suef'],
            '23' => ['name_ar' => 'الفيوم',               'name_en' => 'Fayoum'],
            '24' => ['name_ar' => 'المنيا',               'name_en' => 'Minya'],
            '25' => ['name_ar' => 'أسيوط',                'name_en' => 'Asyut'],
            '26' => ['name_ar' => 'سوهاج',                'name_en' => 'Sohag'],
            '27' => ['name_ar' => 'قنا',                  'name_en' => 'Qena'],
            '28' => ['name_ar' => 'أسوان',                'name_en' => 'Aswan'],
            '29' => ['name_ar' => 'الأقصر',               'name_en' => 'Luxor'],
            '31' => ['name_ar' => 'البحر الأحمر',         'name_en' => 'Red Sea'],
            '32' => ['name_ar' => 'الوادي الجديد',        'name_en' => 'New Valley'],
            '33' => ['name_ar' => 'مطروح',                'name_en' => 'Matrouh'],
            '34' => ['name_ar' => 'شمال سيناء',           'name_en' => 'North Sinai'],
            '35' => ['name_ar' => 'جنوب سيناء',           'name_en' => 'South Sinai'],
            '88' => ['name_ar' => 'خارج مصر',             'name_en' => 'Outside Egypt'],
        ];

        return $governorates[$code] ?? null;
    }
}