<?php
namespace Domain\Client;

class ClientRepository
{
    private const MAX_PER_PAGE = 100;

    private $db;

    public function __construct($db)
    {
        $this->db = $db;
    }

    /**
     * @return array{items: array<int, array<string, mixed>>, total: int}
     */
    public function listForAdmin(
        array $filters,
        string $sortColumn = 'client_surname',
        string $sortDir = 'ASC',
        int $page = 1,
        int $perPage = 15
    ): array {
        $allowedSortColumns = [
            'client_name' => 'c.client_name',
            'client_surname' => 'c.client_surname',
            'client_email' => 'c.client_email',
            'client_phone' => 'c.client_phone',
            'client_city' => 'c.client_city',
            'total_orders' => 'c.total_orders',
            'total_spent' => 'c.total_spent',
            'client_type' => 'c.is_registered',
        ];

        $sortSql = $allowedSortColumns[$sortColumn] ?? 'c.client_surname';
        $sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
        $page = max(1, $page);
        $perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
        $offset = ($page - 1) * $perPage;

        $where = [
            'o.client_name IS NOT NULL',
            'o.client_surname IS NOT NULL',
            'o.client_email IS NOT NULL',
        ];
        $params = [];

        $name = $this->normalizeTextFilter($filters['name'] ?? '');
        if ($name !== '') {
            $where[] = 'o.client_name LIKE :name';
            $params[':name'] = '%' . $name . '%';
        }

        $surname = $this->normalizeTextFilter($filters['surname'] ?? '');
        if ($surname !== '') {
            $where[] = 'o.client_surname LIKE :surname';
            $params[':surname'] = '%' . $surname . '%';
        }

        $email = $this->normalizeTextFilter($filters['email'] ?? '');
        if ($email !== '') {
            $where[] = 'o.client_email LIKE :email';
            $params[':email'] = '%' . $email . '%';
        }

        $clientType = trim((string)($filters['client_type'] ?? ''));
        if ($clientType === 'registered') {
            $where[] = 'o.client_id IS NOT NULL';
        } elseif ($clientType === 'guest') {
            $where[] = 'o.client_id IS NULL';
        }

        $whereSql = implode(' AND ', $where);

        $aggregatedSql = "
            SELECT
                MAX(o.client_id) AS client_id,
                o.client_name,
                o.client_surname,
                o.client_email,
                MAX(o.client_phone) AS client_phone,
                MAX(o.client_city) AS client_city,
                COUNT(*) AS total_orders,
                COALESCE(SUM(o.summary), 0) AS total_spent,
                CASE
                    WHEN MAX(o.client_id) IS NOT NULL THEN 1
                    ELSE 0
                END AS is_registered
            FROM pp_shop_orders AS o
            WHERE {$whereSql}
            GROUP BY o.client_name, o.client_surname, o.client_email
        ";

        $sqlCount = "
            SELECT COUNT(0)
            FROM ({$aggregatedSql}) AS c
        ";

        $stmtCount = $this->db->query($sqlCount, $params);
        $countRows = $stmtCount ? $stmtCount->fetchAll() : [];
        $total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;

        $sql = "
            SELECT
                c.client_id,
                c.client_name,
                c.client_surname,
                c.client_email,
                c.client_phone,
                c.client_city,
                c.total_orders,
                c.total_spent,
                c.is_registered
            FROM ({$aggregatedSql}) AS c
            ORDER BY {$sortSql} {$sortDir}, c.client_surname ASC, c.client_name ASC
            LIMIT {$perPage} OFFSET {$offset}
        ";

        $stmt = $this->db->query($sql, $params);
        $items = $stmt ? $stmt->fetchAll() : [];

        if (!is_array($items)) {
            $items = [];
        }

        foreach ($items as &$item) {
            $item['client_id'] = !isset($item['client_id']) ? null : (int)$item['client_id'];
            $item['client_name'] = (string)($item['client_name'] ?? '');
            $item['client_surname'] = (string)($item['client_surname'] ?? '');
            $item['client_email'] = (string)($item['client_email'] ?? '');
            $item['client_phone'] = (string)($item['client_phone'] ?? '');
            $item['client_city'] = (string)($item['client_city'] ?? '');
            $item['total_orders'] = (int)($item['total_orders'] ?? 0);
            $item['total_spent'] = (float)($item['total_spent'] ?? 0);
            $item['is_registered'] = ((int)($item['is_registered'] ?? 0)) === 1 ? 1 : 0;
        }
        unset($item);

        return [
            'items' => $items,
            'total' => $total,
        ];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function ordersForClient(string $name, string $surname, string $email): array
    {
        $name = trim($name);
        $surname = trim($surname);
        $email = trim($email);

        if ($name === '' || $surname === '' || $email === '') {
            return [];
        }

        $sql = "
            SELECT
                o.id,
                o.date_order,
                o.summary,
                o.payment_method,
                o.transport,
                o.message
            FROM pp_shop_orders AS o
            WHERE o.client_name = :name
                AND o.client_surname = :surname
                AND o.client_email = :email
            ORDER BY o.date_order DESC, o.id DESC
        ";

        $stmt = $this->db->query($sql, [
            ':name' => $name,
            ':surname' => $surname,
            ':email' => $email,
        ]);

        $rows = $stmt ? $stmt->fetchAll() : [];
        if (!is_array($rows)) {
            return [];
        }

        foreach ($rows as &$row) {
            $row['id'] = (int)($row['id'] ?? 0);
            $row['date_order'] = (string)($row['date_order'] ?? '');
            $row['summary'] = (float)($row['summary'] ?? 0);
            $row['payment_method'] = (string)($row['payment_method'] ?? '');
            $row['transport'] = (string)($row['transport'] ?? '');
            $row['message'] = (string)($row['message'] ?? '');
        }
        unset($row);

        return $rows;
    }

    /**
     * @return array{total_orders: int, total_spent: float}
     */
    public function totalsForClient(string $name, string $surname, string $email): array
    {
        $name = trim($name);
        $surname = trim($surname);
        $email = trim($email);

        if ($name === '' || $surname === '' || $email === '') {
            return [
                'total_orders' => 0,
                'total_spent' => 0.0,
            ];
        }

        $sql = "
            SELECT
                COUNT(*) AS total_orders,
                COALESCE(SUM(o.summary), 0) AS total_spent
            FROM pp_shop_orders AS o
            WHERE o.client_name = :name
                AND o.client_surname = :surname
                AND o.client_email = :email
        ";

        $stmt = $this->db->query($sql, [
            ':name' => $name,
            ':surname' => $surname,
            ':email' => $email,
        ]);
        $rows = $stmt ? $stmt->fetchAll() : [];

        return [
            'total_orders' => isset($rows[0]['total_orders']) ? (int)$rows[0]['total_orders'] : 0,
            'total_spent' => isset($rows[0]['total_spent']) ? (float)$rows[0]['total_spent'] : 0.0,
        ];
    }

    private function normalizeTextFilter($value): string
    {
        $value = trim((string)$value);
        if ($value === '') {
            return '';
        }

        if (strlen($value) > 255) {
            return substr($value, 0, 255);
        }

        return $value;
    }
}
