<?php

namespace App\Support;

use App\Models\Business;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Throwable;

final class PhoneNumber
{
    public const DEFAULT_COUNTRY_CODE = '255';

    public static function countryCode(): string
    {
        try {
            if (Schema::hasTable('businesses') && Schema::hasColumn('businesses', 'phone_country_code')) {
                return self::sanitizeCountryCode(Business::query()->whereKey(1)->value('phone_country_code'));
            }
        } catch (Throwable) {
            // During installation/migrations the businesses table or column may not exist yet.
        }

        return self::DEFAULT_COUNTRY_CODE;
    }

    public static function sanitizeCountryCode(mixed $value): string
    {
        $digits = preg_replace('/\D+/', '', (string) $value) ?? '';

        return preg_match('/^[1-9]\d{0,3}$/', $digits) === 1
            ? $digits
            : self::DEFAULT_COUNTRY_CODE;
    }

    public static function normalize(mixed $value, ?string $countryCode = null): ?string
    {
        if ($value === null || trim((string) $value) === '') {
            return null;
        }

        $digits = preg_replace('/\D+/', '', (string) $value) ?? '';
        $countryCode = self::sanitizeCountryCode($countryCode ?? self::countryCode());

        if (str_starts_with($digits, $countryCode)) {
            $local = substr($digits, strlen($countryCode));
            return preg_match('/^[67]\d{8}$/', $local) === 1 ? $countryCode.$local : null;
        }

        if (preg_match('/^0[67]\d{8}$/', $digits) === 1) {
            return $countryCode.substr($digits, 1);
        }

        if (preg_match('/^[67]\d{8}$/', $digits) === 1) {
            return $countryCode.$digits;
        }

        return null;
    }

    public static function local(mixed $value, ?string $countryCode = null): ?string
    {
        $normalized = self::normalize($value, $countryCode);
        if ($normalized === null) {
            return null;
        }

        $countryCode = self::sanitizeCountryCode($countryCode ?? self::countryCode());

        return substr($normalized, strlen($countryCode));
    }

    public static function isValidCountryCode(mixed $value): bool
    {
        return preg_match('/^[1-9]\d{0,3}$/', trim((string) $value)) === 1;
    }

    public static function rebaseStoredNumbers(string $oldCountryCode, string $newCountryCode): void
    {
        $oldCountryCode = self::sanitizeCountryCode($oldCountryCode);
        $newCountryCode = self::sanitizeCountryCode($newCountryCode);

        if ($oldCountryCode === $newCountryCode) {
            return;
        }

        foreach ([
            'businesses' => ['phone', 'phone_alt'],
            'branches' => ['phone'],
            'users' => ['phone'],
            'customers' => ['phone', 'alternative_phone'],
            'next_of_kin' => ['phone'],
            'guarantors' => ['phone'],
        ] as $table => $columns) {
            if (! Schema::hasTable($table)) {
                continue;
            }

            foreach ($columns as $column) {
                if (! Schema::hasColumn($table, $column)) {
                    continue;
                }

                DB::table($table)
                    ->select('id', $column)
                    ->whereNotNull($column)
                    ->orderBy('id')
                    ->chunkById(250, function ($rows) use ($table, $column, $oldCountryCode, $newCountryCode): void {
                        foreach ($rows as $row) {
                            $digits = preg_replace('/\D+/', '', (string) $row->{$column}) ?? '';
                            if (! str_starts_with($digits, $oldCountryCode)) {
                                continue;
                            }

                            $local = substr($digits, strlen($oldCountryCode));
                            if (preg_match('/^[67]\d{8}$/', $local) !== 1) {
                                continue;
                            }

                            DB::table($table)->where('id', $row->id)->update([$column => $newCountryCode.$local]);
                        }
                    });
            }
        }
    }
}
