<?php
declare(strict_types=1);

namespace App\Modules\Settings;

use App\Core\Support\StringHelper;
use PDO;

final class AllegroStatusMappingRepository
{
    public function __construct(private readonly PDO $pdo)
    {
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function listMappings(): array
    {
        $statement = $this->pdo->query(
            'SELECT id, allegro_status_code, allegro_status_name, orderpro_status_code, created_at, updated_at
            FROM allegro_order_status_mappings
            ORDER BY orderpro_status_code ASC, allegro_status_code ASC'
        );
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
        if (!is_array($rows)) {
            return [];
        }

        return array_map(static function (array $row): array {
            return [
                'id' => (int) ($row['id'] ?? 0),
                'allegro_status_code' => strtolower(trim((string) ($row['allegro_status_code'] ?? ''))),
                'allegro_status_name' => trim((string) ($row['allegro_status_name'] ?? '')),
                'orderpro_status_code' => strtolower(trim((string) ($row['orderpro_status_code'] ?? ''))),
                'created_at' => (string) ($row['created_at'] ?? ''),
                'updated_at' => (string) ($row['updated_at'] ?? ''),
            ];
        }, $rows);
    }

    /**
     * @return array<int, array{code:string,name:string}>
     */
    public function listExternalStatuses(): array
    {
        $statement = $this->pdo->query(
            'SELECT DISTINCT allegro_status_code, allegro_status_name
            FROM allegro_order_status_mappings
            WHERE allegro_status_code IS NOT NULL
              AND allegro_status_code <> ""
            ORDER BY allegro_status_code ASC'
        );
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
        if (!is_array($rows)) {
            return [];
        }

        $result = [];
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }

            $code = strtolower(trim((string) ($row['allegro_status_code'] ?? '')));
            if ($code === '') {
                continue;
            }

            $result[] = [
                'code' => $code,
                'name' => trim((string) ($row['allegro_status_name'] ?? $code)),
            ];
        }

        return $result;
    }

    public function upsertMapping(string $orderproStatusCode, ?string $allegroStatusCode, ?string $allegroStatusName): void
    {
        $orderproCode = strtolower(trim($orderproStatusCode));
        if ($orderproCode === '') {
            return;
        }

        $allegroCode = $allegroStatusCode !== null ? strtolower(trim($allegroStatusCode)) : null;

        $statement = $this->pdo->prepare(
            'INSERT INTO allegro_order_status_mappings (
                orderpro_status_code, allegro_status_code, allegro_status_name, created_at, updated_at
            ) VALUES (
                :orderpro_status_code, :allegro_status_code, :allegro_status_name, NOW(), NOW()
            )
            ON DUPLICATE KEY UPDATE
                allegro_status_code = VALUES(allegro_status_code),
                allegro_status_name = VALUES(allegro_status_name),
                updated_at = VALUES(updated_at)'
        );
        $statement->execute([
            'orderpro_status_code' => $orderproCode,
            'allegro_status_code' => $allegroCode !== null && $allegroCode !== '' ? $allegroCode : null,
            'allegro_status_name' => StringHelper::nullableString((string) $allegroStatusName),
        ]);
    }

    public function upsertDiscoveredStatus(string $allegroStatusCode, ?string $allegroStatusName): void
    {
        $code = strtolower(trim($allegroStatusCode));
        if ($code === '') {
            return;
        }

        $existing = $this->pdo->prepare(
            'SELECT id FROM allegro_order_status_mappings
            WHERE allegro_status_code = :allegro_status_code
            LIMIT 1'
        );
        $existing->execute(['allegro_status_code' => $code]);

        if ($existing->fetchColumn() !== false) {
            $update = $this->pdo->prepare(
                'UPDATE allegro_order_status_mappings
                SET allegro_status_name = CASE
                        WHEN :allegro_status_name IS NULL OR :allegro_status_name2 = "" THEN allegro_status_name
                        ELSE :allegro_status_name3
                    END,
                    updated_at = NOW()
                WHERE allegro_status_code = :allegro_status_code'
            );
            $name = StringHelper::nullableString((string) $allegroStatusName);
            $update->execute([
                'allegro_status_name' => $name,
                'allegro_status_name2' => (string) $name,
                'allegro_status_name3' => $name,
                'allegro_status_code' => $code,
            ]);
            return;
        }

        $insert = $this->pdo->prepare(
            'INSERT INTO allegro_order_status_mappings (
                allegro_status_code, allegro_status_name, orderpro_status_code, created_at, updated_at
            ) VALUES (
                :allegro_status_code, :allegro_status_name, NULL, NOW(), NOW()
            )'
        );
        $insert->execute([
            'allegro_status_code' => $code,
            'allegro_status_name' => StringHelper::nullableString((string) $allegroStatusName),
        ]);
    }

    public function deleteMappingById(int $id): void
    {
        if ($id <= 0) {
            return;
        }

        $statement = $this->pdo->prepare('DELETE FROM allegro_order_status_mappings WHERE id = :id');
        $statement->execute(['id' => $id]);
    }

    public function findMappedOrderproStatusCode(string $allegroStatusCode): ?string
    {
        $code = strtolower(trim($allegroStatusCode));
        if ($code === '') {
            return null;
        }

        $statement = $this->pdo->prepare(
            'SELECT orderpro_status_code
            FROM allegro_order_status_mappings
            WHERE allegro_status_code = :allegro_status_code
              AND orderpro_status_code IS NOT NULL
              AND orderpro_status_code <> ""
            LIMIT 1'
        );
        $statement->execute(['allegro_status_code' => $code]);
        $value = $statement->fetchColumn();
        if (!is_string($value)) {
            return null;
        }

        $mapped = strtolower(trim($value));
        return $mapped !== '' ? $mapped : null;
    }

    /**
     * @return array<string, string> orderpro_status_code => allegro_status_code
     */
    public function buildOrderproToAllegroMap(): array
    {
        $statement = $this->pdo->query(
            'SELECT orderpro_status_code, allegro_status_code
            FROM allegro_order_status_mappings
            WHERE orderpro_status_code IS NOT NULL
              AND orderpro_status_code <> ""
              AND allegro_status_code IS NOT NULL
              AND allegro_status_code <> ""
            ORDER BY id ASC'
        );
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
        if (!is_array($rows)) {
            return [];
        }

        $map = [];
        foreach ($rows as $row) {
            $orderproCode = strtolower(trim((string) ($row['orderpro_status_code'] ?? '')));
            $allegroCode = strtolower(trim((string) ($row['allegro_status_code'] ?? '')));
            if ($orderproCode === '' || $allegroCode === '') {
                continue;
            }

            if (!isset($map[$orderproCode])) {
                $map[$orderproCode] = $allegroCode;
            }
        }

        return $map;
    }

    /**
     * @return array<string, string> allegro_status_code => orderpro_status_code
     */
    public function buildAllegroToOrderproMap(): array
    {
        $statement = $this->pdo->query(
            'SELECT allegro_status_code, orderpro_status_code
            FROM allegro_order_status_mappings
            WHERE allegro_status_code IS NOT NULL
              AND allegro_status_code <> ""
              AND orderpro_status_code IS NOT NULL
              AND orderpro_status_code <> ""
            ORDER BY id ASC'
        );
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
        if (!is_array($rows)) {
            return [];
        }

        $map = [];
        foreach ($rows as $row) {
            $allegroCode = strtolower(trim((string) ($row['allegro_status_code'] ?? '')));
            $orderproCode = strtolower(trim((string) ($row['orderpro_status_code'] ?? '')));
            if ($allegroCode === '' || $orderproCode === '') {
                continue;
            }

            if (!isset($map[$allegroCode])) {
                $map[$allegroCode] = $orderproCode;
            }
        }

        return $map;
    }

    /**
     * @param array<int, array{orderpro_status_code:string,allegro_status_code:string,allegro_status_name:string}> $mappings
     */
    public function replaceAllMappings(array $mappings): void
    {
        $this->pdo->exec(
            'DELETE FROM allegro_order_status_mappings WHERE orderpro_status_code IS NOT NULL AND orderpro_status_code <> ""'
        );

        if ($mappings === []) {
            return;
        }

        $insertStatement = $this->pdo->prepare(
            'INSERT INTO allegro_order_status_mappings (
                orderpro_status_code, allegro_status_code, allegro_status_name, created_at, updated_at
            ) VALUES (
                :orderpro_status_code, :allegro_status_code, :allegro_status_name, NOW(), NOW()
            )'
        );

        foreach ($mappings as $mapping) {
            $orderproCode = strtolower(trim((string) ($mapping['orderpro_status_code'] ?? '')));
            $allegroCode = strtolower(trim((string) ($mapping['allegro_status_code'] ?? '')));
            if ($orderproCode === '' || $allegroCode === '') {
                continue;
            }

            $insertStatement->execute([
                'orderpro_status_code' => $orderproCode,
                'allegro_status_code' => $allegroCode,
                'allegro_status_name' => trim((string) ($mapping['allegro_status_name'] ?? '')) ?: null,
            ]);
        }
    }
}
