<?php
declare(strict_types=1);

namespace App\Modules\Automation;

use App\Core\Http\ToggleableRepositoryTrait;
use PDO;
use Throwable;

final class AutomationRepository
{
    use ToggleableRepositoryTrait {
        toggleActive as private traitToggleActive;
    }
    public function __construct(
        private readonly PDO $pdo
    ) {
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function findAll(): array
    {
        $sql = '
            SELECT r.id, r.name, r.event_type, r.is_active, r.created_at, r.updated_at,
                   (SELECT COUNT(*) FROM automation_conditions WHERE rule_id = r.id) AS conditions_count,
                   (SELECT COUNT(*) FROM automation_actions WHERE rule_id = r.id) AS actions_count
            FROM automation_rules r
            ORDER BY r.name ASC, r.id DESC
        ';
        $statement = $this->pdo->prepare($sql);
        $statement->execute();
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        return is_array($rows) ? $rows : [];
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findById(int $id): ?array
    {
        $statement = $this->pdo->prepare('SELECT * FROM automation_rules WHERE id = :id LIMIT 1');
        $statement->execute(['id' => $id]);
        $rule = $statement->fetch(PDO::FETCH_ASSOC);

        if (!is_array($rule)) {
            return null;
        }

        $rule['conditions'] = $this->loadConditions($id);
        $rule['actions'] = $this->loadActions($id);

        return $rule;
    }

    /**
     * @param array<string, mixed> $data
     * @param list<array<string, mixed>> $conditions
     * @param list<array<string, mixed>> $actions
     */
    public function create(array $data, array $conditions, array $actions): int
    {
        $this->pdo->beginTransaction();

        try {
            $statement = $this->pdo->prepare(
                'INSERT INTO automation_rules (name, event_type, is_active)
                 VALUES (:name, :event_type, :is_active)'
            );
            $statement->execute([
                'name' => $data['name'],
                'event_type' => $data['event_type'],
                'is_active' => (int) ($data['is_active'] ?? 1),
            ]);

            $ruleId = (int) $this->pdo->lastInsertId();

            $this->insertConditions($ruleId, $conditions);
            $this->insertActions($ruleId, $actions);

            $this->pdo->commit();

            return $ruleId;
        } catch (Throwable $e) {
            $this->pdo->rollBack();
            throw $e;
        }
    }

    /**
     * @param array<string, mixed> $data
     * @param list<array<string, mixed>> $conditions
     * @param list<array<string, mixed>> $actions
     */
    public function update(int $id, array $data, array $conditions, array $actions): bool
    {
        $this->pdo->beginTransaction();

        try {
            $statement = $this->pdo->prepare(
                'UPDATE automation_rules SET name = :name, event_type = :event_type, is_active = :is_active WHERE id = :id'
            );
            $statement->execute([
                'id' => $id,
                'name' => $data['name'],
                'event_type' => $data['event_type'],
                'is_active' => (int) ($data['is_active'] ?? 1),
            ]);

            $this->pdo->prepare('DELETE FROM automation_conditions WHERE rule_id = :id')->execute(['id' => $id]);
            $this->pdo->prepare('DELETE FROM automation_actions WHERE rule_id = :id')->execute(['id' => $id]);

            $this->insertConditions($id, $conditions);
            $this->insertActions($id, $actions);

            $this->pdo->commit();

            return true;
        } catch (Throwable $e) {
            $this->pdo->rollBack();
            throw $e;
        }
    }

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

    public function duplicate(int $id): int
    {
        $source = $this->findById($id);
        if ($source === null) {
            throw new \RuntimeException('Zadanie nie istnieje');
        }

        $conditions = array_map(static function (array $c): array {
            return ['type' => $c['condition_type'], 'value' => $c['condition_value']];
        }, $source['conditions']);

        $actions = array_map(static function (array $a): array {
            return ['type' => $a['action_type'], 'config' => $a['action_config']];
        }, $source['actions']);

        return $this->create(
            ['name' => 'Kopia — ' . $source['name'], 'event_type' => $source['event_type'], 'is_active' => 0],
            $conditions,
            $actions
        );
    }

    public function toggleActive(int $id): void
    {
        $this->traitToggleActive('automation_rules', $id);
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function findActiveByEvent(string $eventType): array
    {
        $statement = $this->pdo->prepare(
            'SELECT id, name, event_type FROM automation_rules WHERE event_type = :event_type AND is_active = 1'
        );
        $statement->execute(['event_type' => $eventType]);
        $rules = $statement->fetchAll(PDO::FETCH_ASSOC);

        if (!is_array($rules)) {
            return [];
        }

        foreach ($rules as &$rule) {
            $ruleId = (int) $rule['id'];
            $rule['conditions'] = $this->loadConditions($ruleId);
            $rule['actions'] = $this->loadActions($ruleId);
        }
        unset($rule);

        return $rules;
    }

    /**
     * @return list<array{id: int, type: string, name: string}>
     */
    public function listOrderIntegrations(): array
    {
        $statement = $this->pdo->prepare(
            "SELECT id, type, name FROM integrations WHERE type IN ('allegro', 'shoppro') AND is_active = 1 ORDER BY type, name"
        );
        $statement->execute();
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        return is_array($rows) ? $rows : [];
    }

    /**
     * @return list<array{id: int, name: string}>
     */
    public function listEmailTemplates(): array
    {
        $statement = $this->pdo->prepare(
            'SELECT id, name FROM email_templates WHERE is_active = 1 ORDER BY name'
        );
        $statement->execute();
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        return is_array($rows) ? $rows : [];
    }

    /**
     * @return list<array{id:int,name:string}>
     */
    public function listRuleOptions(): array
    {
        $statement = $this->pdo->prepare(
            'SELECT id, name FROM automation_rules ORDER BY name ASC'
        );
        $statement->execute();
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        return is_array($rows) ? $rows : [];
    }

    /**
     * @return list<array{code:string,name:string}>
     */
    public function listActiveOrderStatuses(): array
    {
        $statement = $this->pdo->prepare(
            'SELECT code, name FROM order_statuses WHERE is_active = 1 ORDER BY name ASC, id ASC'
        );
        $statement->execute();
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        return is_array($rows) ? $rows : [];
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function loadConditions(int $ruleId): array
    {
        $statement = $this->pdo->prepare(
            'SELECT id, condition_type, condition_value, sort_order FROM automation_conditions WHERE rule_id = :rule_id ORDER BY sort_order'
        );
        $statement->execute(['rule_id' => $ruleId]);
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        if (!is_array($rows)) {
            return [];
        }

        foreach ($rows as &$row) {
            $decoded = json_decode((string) ($row['condition_value'] ?? '{}'), true);
            $row['condition_value'] = is_array($decoded) ? $decoded : [];
        }
        unset($row);

        return $rows;
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function loadActions(int $ruleId): array
    {
        $statement = $this->pdo->prepare(
            'SELECT id, action_type, action_config, sort_order FROM automation_actions WHERE rule_id = :rule_id ORDER BY sort_order'
        );
        $statement->execute(['rule_id' => $ruleId]);
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        if (!is_array($rows)) {
            return [];
        }

        foreach ($rows as &$row) {
            $decoded = json_decode((string) ($row['action_config'] ?? '{}'), true);
            $row['action_config'] = is_array($decoded) ? $decoded : [];
        }
        unset($row);

        return $rows;
    }

    /**
     * @param list<array<string, mixed>> $conditions
     */
    private function insertConditions(int $ruleId, array $conditions): void
    {
        $statement = $this->pdo->prepare(
            'INSERT INTO automation_conditions (rule_id, condition_type, condition_value, sort_order)
             VALUES (:rule_id, :condition_type, :condition_value, :sort_order)'
        );

        foreach ($conditions as $index => $condition) {
            $statement->execute([
                'rule_id' => $ruleId,
                'condition_type' => $condition['type'],
                'condition_value' => json_encode($condition['value'], JSON_UNESCAPED_UNICODE),
                'sort_order' => $index,
            ]);
        }
    }

    /**
     * @param list<array<string, mixed>> $actions
     */
    private function insertActions(int $ruleId, array $actions): void
    {
        $statement = $this->pdo->prepare(
            'INSERT INTO automation_actions (rule_id, action_type, action_config, sort_order)
             VALUES (:rule_id, :action_type, :action_config, :sort_order)'
        );

        foreach ($actions as $index => $action) {
            $statement->execute([
                'rule_id' => $ruleId,
                'action_type' => $action['type'],
                'action_config' => json_encode($action['config'], JSON_UNESCAPED_UNICODE),
                'sort_order' => $index,
            ]);
        }
    }
}
