<?php

namespace MintyPHP\Module\Bookmarks\Repository;

use MintyPHP\DB;
use MintyPHP\Repository\Support\RepoQuery;
use MintyPHP\Repository\Support\RepositoryArrayHelper;

class BookmarkRepository
{
    use SortOrderTrait;

    protected function sortOrderTableName(): string
    {
        return 'user_bookmarks';
    }

    private function unwrapList(mixed $rows): array
    {
        return RepositoryArrayHelper::unwrapList($rows, 'user_bookmarks');
    }

    public function listByUser(int $userId, array $options = []): array
    {
        if ($userId <= 0) {
            return [];
        }
        [$limit, $offset] = RepoQuery::sanitizeLimitOffset($options, 100, 1, 100);
        $rows = DB::select(
            'select id, user_id, group_id, name, url, sort_order, created, modified from user_bookmarks where user_id = ? order by sort_order, id limit ? offset ?',
            (string) $userId,
            (string) $limit,
            (string) $offset
        );
        return $this->unwrapList($rows);
    }

    public function nextBookmarkSortOrder(int $userId, ?int $groupId): int
    {
        if ($userId <= 0) {
            return 1;
        }

        if ($groupId !== null && $groupId > 0) {
            $max = DB::selectValue(
                'select COALESCE(max(sort_order), 0) from user_bookmarks where user_id = ? and group_id = ?',
                (string) $userId,
                (string) $groupId
            );
            return max(1, ((int) $max) + 1);
        }

        $max = DB::selectValue(
            'select COALESCE(max(sort_order), 0) from user_bookmarks where user_id = ? and group_id is NULL',
            (string) $userId
        );
        return max(1, ((int) $max) + 1);
    }

    public function countByUser(int $userId): int
    {
        if ($userId <= 0) {
            return 0;
        }
        $count = DB::selectValue(
            'select count(*) from user_bookmarks where user_id = ?',
            (string) $userId
        );
        return $count ? (int) $count : 0;
    }

    public function findById(int $id, int $userId): array|false
    {
        if ($id <= 0 || $userId <= 0) {
            return false;
        }
        $rows = DB::select(
            'select id, user_id, group_id, name, url, sort_order, created, modified from user_bookmarks where id = ? and user_id = ?',
            (string) $id,
            (string) $userId
        );
        $list = $this->unwrapList($rows);
        return $list[0] ?? false;
    }

    public function findByUserAndUrl(int $userId, string $url): array|false
    {
        $url = trim($url);
        if ($userId <= 0 || $url === '') {
            return false;
        }

        $rows = DB::select(
            'select id, user_id, group_id, name, url, sort_order, created, modified from user_bookmarks where user_id = ? and url = ? order by id desc limit 1',
            (string) $userId,
            $url
        );
        $list = $this->unwrapList($rows);
        return $list[0] ?? false;
    }

    public function create(array $data): int|false
    {
        $userId = (int) ($data['user_id'] ?? 0);
        $name = trim((string) ($data['name'] ?? ''));
        $url = trim((string) ($data['url'] ?? ''));
        $groupId = isset($data['group_id']) ? (int) $data['group_id'] : null;
        $sortOrder = (int) ($data['sort_order'] ?? 0);

        if ($userId <= 0 || $name === '' || $url === '') {
            return false;
        }

        $groupIdParam = ($groupId !== null && $groupId > 0) ? (string) $groupId : null;

        return DB::insert(
            'insert into user_bookmarks (user_id, group_id, name, url, sort_order, created) values (?,?,?,?,?,NOW())',
            (string) $userId,
            $groupIdParam,
            $name,
            $url,
            (string) $sortOrder
        );
    }

    public function update(int $id, int $userId, array $data): bool
    {
        if ($id <= 0 || $userId <= 0) {
            return false;
        }

        $sets = [];
        $params = [];

        if (array_key_exists('name', $data)) {
            $name = trim((string) $data['name']);
            if ($name === '') {
                return false;
            }
            $sets[] = 'name = ?';
            $params[] = $name;
        }
        if (array_key_exists('group_id', $data)) {
            $sets[] = 'group_id = ?';
            $groupId = $data['group_id'];
            $params[] = ($groupId !== null && (int) $groupId > 0) ? (string) (int) $groupId : null;
        }
        if (array_key_exists('url', $data)) {
            $url = trim((string) $data['url']);
            if ($url === '') {
                return false;
            }
            $sets[] = 'url = ?';
            $params[] = $url;
        }
        if (array_key_exists('sort_order', $data)) {
            $sortOrder = (int) $data['sort_order'];
            if ($sortOrder <= 0) {
                return false;
            }
            $sets[] = 'sort_order = ?';
            $params[] = (string) $sortOrder;
        }

        if ($sets === []) {
            return false;
        }

        $params[] = (string) $id;
        $params[] = (string) $userId;

        $affected = DB::update(
            'update user_bookmarks set ' . implode(', ', $sets) . ' where id = ? and user_id = ?',
            ...$params
        );
        return $affected !== false;
    }

    public function delete(int $id, int $userId): bool
    {
        if ($id <= 0 || $userId <= 0) {
            return false;
        }
        $deleted = DB::delete(
            'delete from user_bookmarks where id = ? and user_id = ?',
            (string) $id,
            (string) $userId
        );
        return $deleted !== false && (int) $deleted > 0;
    }

    /** @return list<array<string, mixed>> */
    public function listByGroup(int $userId, int $groupId): array
    {
        if ($userId <= 0 || $groupId <= 0) {
            return [];
        }
        $rows = DB::select(
            'select id, user_id, group_id, name, url, sort_order from user_bookmarks where user_id = ? and group_id = ? order by sort_order, id',
            (string) $userId,
            (string) $groupId
        );
        return $this->unwrapList($rows);
    }

    public function unsetGroupId(int $bookmarkId, int $userId, int $sortOrder): bool
    {
        if ($bookmarkId <= 0 || $userId <= 0 || $sortOrder <= 0) {
            return false;
        }
        $updated = DB::update(
            'update user_bookmarks set group_id = NULL, sort_order = ? where id = ? and user_id = ?',
            (string) $sortOrder,
            (string) $bookmarkId,
            (string) $userId
        );
        return $updated !== false;
    }
}
