<?php

namespace App\Services;

use Carbon\Carbon;
use InvalidArgumentException;

class NumerologyCalculationService
{
    private array $monthMapping = [
        // English months
        'january' => 1, 'february' => 2, 'march' => 3, 'april' => 4,
        'may' => 5, 'june' => 6, 'july' => 7, 'august' => 8,
        'september' => 9, 'october' => 10, 'november' => 11, 'december' => 12,
        // Malay months
        'januari' => 1, 'februari' => 2, 'mac' => 3, 'april' => 4,
        'mei' => 5, 'jun' => 6, 'julai' => 7, 'ogos' => 8,
        'september' => 9, 'oktober' => 10, 'november' => 11, 'disember' => 12,
    ];

    public function calculateNumerology(string $name, string $birthdate, ?string $industry = null): array
    {
        $parsedDate = $this->parseDate($birthdate);
        $this->validateDate($parsedDate);

        // Extract date components
        $day = $parsedDate['day'];
        $month = $parsedDate['month'];
        $year = $parsedDate['year'];

        // Split year into first two digits and last two digits
        $yearFirstTwo = intval(substr($year, 0, 2));
        $yearLastTwo = intval(substr($year, -2));

        // L5: Raw birthdate components
        $L5 = [$day, $month, $yearFirstTwo, $yearLastTwo];

        // L6: Digit reduction with special rule
        $L6 = [
            $this->reduce($L5[0]),
            $this->reduce($L5[1]),
            $this->reduce($L5[2]),
            $L5[3] == 0 ? 5 : $this->reduce($L5[3])  // Special case for years ending in '00'
        ];

        // L7: Pairwise sums
        $L7 = [
            $this->reduce($L6[0] + $L6[1]),
            $this->reduce($L6[2] + $L6[3])
        ];

        // L8: Complex calculation (7 elements)
        $A = $this->reduce($L6[1] + $L7[0]);
        $B = $this->reduce($L6[0] + $L7[0]);
        $C = $this->reduce($A + $B);
        $D = $this->reduce($L6[2] + $L7[1]);
        $E = $this->reduce($L6[3] + $L7[1]);
        $F = $this->reduce($D + $E);
        $G = $this->reduce($L7[0] + $L7[1]);

        $L8 = [$C, $B, $A, $G, $D, $E, $F];

        // L9: Final calculation
        $Left = $this->reduce($L7[1] + $G);
        $Right = $this->reduce($L7[0] + $G);
        $Middle = $this->reduce($Left + $Right);

        $L9 = [$Left, $Middle, $Right];

        // Root Number = G (middle of L8)
        $rootNumber = $G;

        return [
            'name' => $name,
            'birthdate' => Carbon::createFromDate($year, $month, $day)->format('Y-m-d'),
            'industry' => $industry,
            'L5' => $L5,
            'L6' => $L6,
            'L7' => $L7,
            'L8' => $L8,
            'L9' => $L9,
            'root_number' => $rootNumber,
        ];
    }

    private function parseDate(string $dateString): array
    {
        $dateString = trim($dateString);

        // Handle formats: "31-July-1985", "31/07/1985", "31 July 1985"
        $patterns = [
            // DD-Month-YYYY or DD Month YYYY
            '/^(\d{1,2})[-\s]([a-zA-Z]+)[-\s](\d{4})$/',
            // DD/MM/YYYY
            '/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/',
            // DD-MM-YYYY
            '/^(\d{1,2})-(\d{1,2})-(\d{4})$/',
        ];

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $dateString, $matches)) {
                $day = intval($matches[1]);
                $monthOrNumber = $matches[2];
                $year = intval($matches[3]);

                // If month is a string, convert to number
                if (is_numeric($monthOrNumber)) {
                    $month = intval($monthOrNumber);
                } else {
                    $monthLower = strtolower($monthOrNumber);
                    if (!isset($this->monthMapping[$monthLower])) {
                        throw new InvalidArgumentException("Unknown month: $monthOrNumber");
                    }
                    $month = $this->monthMapping[$monthLower];
                }

                return ['day' => $day, 'month' => $month, 'year' => $year];
            }
        }

        throw new InvalidArgumentException("Invalid date format: $dateString");
    }

    private function validateDate(array $parsedDate): void
    {
        $day = $parsedDate['day'];
        $month = $parsedDate['month'];
        $year = $parsedDate['year'];

        // Validate year range
        if ($year < 1800 || $year > 2099) {
            throw new InvalidArgumentException("Year must be between 1800 and 2099");
        }

        // Validate month
        if ($month < 1 || $month > 12) {
            throw new InvalidArgumentException("Month must be between 1 and 12");
        }

        // Validate day
        if ($day < 1 || $day > 31) {
            throw new InvalidArgumentException("Day must be between 1 and 31");
        }

        // Additional validation using Carbon
        if (!checkdate($month, $day, $year)) {
            throw new InvalidArgumentException("Invalid date: $day/$month/$year");
        }
    }

    private function reduce(int $number): int
    {
        while ($number > 9) {
            $sum = 0;
            while ($number > 0) {
                $sum += $number % 10;
                $number = intval($number / 10);
            }
            $number = $sum;
        }
        return $number;
    }
}