<?php
declare(strict_types=1);

namespace App\Modules\Settings;

use PDO;

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

    /**
     * @return array<int, array<string, mixed>>
     */
    public function listGroups(): array
    {
        $stmt = $this->pdo->query(
            'SELECT id, name, code, color_hex, sort_order, is_active, created_at, updated_at
            FROM order_status_groups
            ORDER BY sort_order ASC, id ASC'
        );
        $rows = $stmt !== false ? $stmt->fetchAll() : [];

        return is_array($rows) ? array_values(array_filter($rows, static fn (mixed $row): bool => is_array($row))) : [];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function listStatuses(): array
    {
        $stmt = $this->pdo->query(
            'SELECT s.id, s.group_id, g.name AS group_name, g.code AS group_code, g.color_hex AS group_color_hex,
                    s.name, s.code, s.sort_order, s.is_active, s.created_at, s.updated_at
            FROM order_statuses s
            INNER JOIN order_status_groups g ON g.id = s.group_id
            ORDER BY g.sort_order ASC, g.id ASC, s.sort_order ASC, s.id ASC'
        );
        $rows = $stmt !== false ? $stmt->fetchAll() : [];

        return is_array($rows) ? array_values(array_filter($rows, static fn (mixed $row): bool => is_array($row))) : [];
    }

    public function groupExists(int $groupId): bool
    {
        $stmt = $this->pdo->prepare('SELECT id FROM order_status_groups WHERE id = :id LIMIT 1');
        $stmt->execute(['id' => $groupId]);

        return (bool) $stmt->fetchColumn();
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findGroupById(int $groupId): ?array
    {
        $stmt = $this->pdo->prepare(
            'SELECT id, name, code, color_hex, sort_order, is_active, created_at, updated_at
            FROM order_status_groups
            WHERE id = :id
            LIMIT 1'
        );
        $stmt->execute(['id' => $groupId]);
        $row = $stmt->fetch();

        return is_array($row) ? $row : null;
    }

    public function existsGroupCode(string $code, int $excludeId = 0): bool
    {
        $stmt = $this->pdo->prepare(
            'SELECT id FROM order_status_groups WHERE code = :code AND id <> :exclude_id LIMIT 1'
        );
        $stmt->execute([
            'code' => $code,
            'exclude_id' => $excludeId,
        ]);

        return (bool) $stmt->fetchColumn();
    }

    public function existsStatusCode(string $code, int $excludeId = 0): bool
    {
        $stmt = $this->pdo->prepare(
            'SELECT id FROM order_statuses WHERE code = :code AND id <> :exclude_id LIMIT 1'
        );
        $stmt->execute([
            'code' => $code,
            'exclude_id' => $excludeId,
        ]);

        return (bool) $stmt->fetchColumn();
    }

    public function statusExists(int $statusId): bool
    {
        $stmt = $this->pdo->prepare('SELECT id FROM order_statuses WHERE id = :id LIMIT 1');
        $stmt->execute(['id' => $statusId]);

        return (bool) $stmt->fetchColumn();
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findStatusById(int $statusId): ?array
    {
        $stmt = $this->pdo->prepare(
            'SELECT id, group_id, name, code, sort_order, is_active, created_at, updated_at
            FROM order_statuses
            WHERE id = :id
            LIMIT 1'
        );
        $stmt->execute(['id' => $statusId]);
        $row = $stmt->fetch();

        return is_array($row) ? $row : null;
    }

    public function createGroup(string $name, string $code, string $colorHex, int $sortOrder, bool $isActive): void
    {
        $stmt = $this->pdo->prepare(
            'INSERT INTO order_status_groups (name, code, color_hex, sort_order, is_active, created_at, updated_at)
            VALUES (:name, :code, :color_hex, :sort_order, :is_active, :created_at, :updated_at)'
        );
        $now = date('Y-m-d H:i:s');
        $stmt->execute([
            'name' => $name,
            'code' => $code,
            'color_hex' => $colorHex,
            'sort_order' => $sortOrder,
            'is_active' => $isActive ? 1 : 0,
            'created_at' => $now,
            'updated_at' => $now,
        ]);
    }

    public function updateGroup(int $id, string $name, string $code, string $colorHex, bool $isActive): void
    {
        $stmt = $this->pdo->prepare(
            'UPDATE order_status_groups
            SET name = :name, code = :code, color_hex = :color_hex, is_active = :is_active, updated_at = :updated_at
            WHERE id = :id'
        );
        $stmt->execute([
            'id' => $id,
            'name' => $name,
            'code' => $code,
            'color_hex' => $colorHex,
            'is_active' => $isActive ? 1 : 0,
            'updated_at' => date('Y-m-d H:i:s'),
        ]);
    }

    public function deleteGroup(int $id): void
    {
        $stmt = $this->pdo->prepare('DELETE FROM order_status_groups WHERE id = :id');
        $stmt->execute(['id' => $id]);
    }

    public function createStatus(int $groupId, string $name, string $code, int $sortOrder, bool $isActive): void
    {
        $stmt = $this->pdo->prepare(
            'INSERT INTO order_statuses (group_id, name, code, sort_order, is_active, created_at, updated_at)
            VALUES (:group_id, :name, :code, :sort_order, :is_active, :created_at, :updated_at)'
        );
        $now = date('Y-m-d H:i:s');
        $stmt->execute([
            'group_id' => $groupId,
            'name' => $name,
            'code' => $code,
            'sort_order' => $sortOrder,
            'is_active' => $isActive ? 1 : 0,
            'created_at' => $now,
            'updated_at' => $now,
        ]);
    }

    public function updateStatus(int $id, int $groupId, string $name, string $code, bool $isActive): void
    {
        $stmt = $this->pdo->prepare(
            'UPDATE order_statuses
            SET group_id = :group_id, name = :name, code = :code, is_active = :is_active, updated_at = :updated_at
            WHERE id = :id'
        );
        $stmt->execute([
            'id' => $id,
            'group_id' => $groupId,
            'name' => $name,
            'code' => $code,
            'is_active' => $isActive ? 1 : 0,
            'updated_at' => date('Y-m-d H:i:s'),
        ]);
    }

    public function deleteStatus(int $id): void
    {
        $stmt = $this->pdo->prepare('DELETE FROM order_statuses WHERE id = :id');
        $stmt->execute(['id' => $id]);
    }

    public function nextGroupSortOrder(): int
    {
        $stmt = $this->pdo->query('SELECT COALESCE(MAX(sort_order), -1) + 1 FROM order_status_groups');
        $value = $stmt !== false ? $stmt->fetchColumn() : 0;

        return max(0, (int) $value);
    }

    public function nextStatusSortOrder(int $groupId): int
    {
        $stmt = $this->pdo->prepare('SELECT COALESCE(MAX(sort_order), -1) + 1 FROM order_statuses WHERE group_id = :group_id');
        $stmt->execute(['group_id' => $groupId]);
        $value = $stmt->fetchColumn();

        return max(0, (int) $value);
    }

    /**
     * @param array<int, int> $orderedIds
     */
    public function reorderGroups(array $orderedIds): void
    {
        if ($orderedIds === []) {
            return;
        }

        $updateStmt = $this->pdo->prepare(
            'UPDATE order_status_groups SET sort_order = :sort_order, updated_at = :updated_at WHERE id = :id'
        );

        $this->pdo->beginTransaction();
        try {
            $now = date('Y-m-d H:i:s');
            foreach (array_values($orderedIds) as $sortOrder => $groupId) {
                $id = max(0, (int) $groupId);
                if ($id <= 0) {
                    continue;
                }

                $updateStmt->execute([
                    'sort_order' => $sortOrder,
                    'updated_at' => $now,
                    'id' => $id,
                ]);
            }
            $this->pdo->commit();
        } catch (\Throwable $exception) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $exception;
        }
    }

    /**
     * @param array<int, int> $orderedIds
     */
    public function reorderStatusesByGroup(int $groupId, array $orderedIds): void
    {
        if ($orderedIds === []) {
            return;
        }

        $updateStmt = $this->pdo->prepare(
            'UPDATE order_statuses
            SET sort_order = :sort_order, updated_at = :updated_at
            WHERE id = :id AND group_id = :group_id'
        );

        $this->pdo->beginTransaction();
        try {
            $now = date('Y-m-d H:i:s');
            foreach (array_values($orderedIds) as $sortOrder => $statusId) {
                $id = max(0, (int) $statusId);
                if ($id <= 0) {
                    continue;
                }

                $updateStmt->execute([
                    'sort_order' => $sortOrder,
                    'updated_at' => $now,
                    'id' => $id,
                    'group_id' => $groupId,
                ]);
            }
            $this->pdo->commit();
        } catch (\Throwable $exception) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $exception;
        }
    }
}
