using BookmarkApi.Core.Interfaces;
using BookmarkApi.Data.Entities;
using BookmarkApi.Shared.DTOs;
using BookmarkApi.Shared.DTOs.Bookmark;
using BookmarkApi.Shared.Enums;

namespace BookmarkApi.Core.Services;

/// <summary>
/// 书签服务实现
/// </summary>
public class BookmarkService : IBookmarkService
{
    private readonly IFreeSql _freeSql;

    public BookmarkService(IFreeSql freeSql)
    {
        _freeSql = freeSql;
    }

    /// <inheritdoc />
    public async Task<List<BookmarkDto>> GetUserBookmarksAsync(Guid userId, Guid deviceId, bool isAdminDevice, string? tag = null)
    {
        var query = _freeSql.Select<Bookmark>()
            .Where(b => b.UserId == userId);

        // 如果不是管理员设备，需要过滤可见性
        if (!isAdminDevice)
        {
            query = query.Where(b =>
                b.Visibility == VisibilityType.Public ||
                (b.Visibility == VisibilityType.Specified && b.AllowedDevices != null && b.AllowedDevices.Contains(deviceId))
            );
        }

        // 按标签筛选
        if (!string.IsNullOrEmpty(tag))
        {
            query = query.Where(b => b.Tags.Contains(tag));
        }

        var bookmarks = await query
            .OrderByDescending(b => b.Order)
            .ToListAsync();

        return bookmarks.Select(MapToDto).ToList();
    }

    /// <inheritdoc />
    public async Task<List<BookmarkDto>> SearchBookmarksAsync(Guid userId, Guid deviceId, bool isAdminDevice, string keyword)
    {
        var query = _freeSql.Select<Bookmark>()
            .Where(b => b.UserId == userId);

        // 如果不是管理员设备，需要过滤可见性
        if (!isAdminDevice)
        {
            query = query.Where(b =>
                b.Visibility == VisibilityType.Public ||
                (b.Visibility == VisibilityType.Specified && b.AllowedDevices != null && b.AllowedDevices.Contains(deviceId))
            );
        }

        // 搜索关键词
        var lowerKeyword = keyword.ToLower();
        query = query.Where(b =>
            b.Title.ToLower().Contains(lowerKeyword) ||
            b.Url.ToLower().Contains(lowerKeyword) ||
            (b.Description != null && b.Description.ToLower().Contains(lowerKeyword))
        );

        var bookmarks = await query
            .OrderByDescending(b => b.VisitCount)
            .OrderByDescending(b => b.Order)
            .Take(20)
            .ToListAsync();

        return bookmarks.Select(MapToDto).ToList();
    }

    /// <inheritdoc />
    public async Task<BookmarkDto?> GetBookmarkAsync(Guid bookmarkId, Guid userId, Guid deviceId, bool isAdminDevice)
    {
        var bookmark = await _freeSql.Select<Bookmark>()
            .Where(b => b.Id == bookmarkId && b.UserId == userId)
            .FirstAsync();

        if (bookmark == null)
        {
            return null;
        }

        // 检查可见性
        if (!isAdminDevice)
        {
            if (bookmark.Visibility == VisibilityType.Private)
            {
                return null;
            }

            if (bookmark.Visibility == VisibilityType.Specified &&
                (bookmark.AllowedDevices == null || !bookmark.AllowedDevices.Contains(deviceId)))
            {
                return null;
            }
        }

        return MapToDto(bookmark);
    }

    /// <inheritdoc />
    public async Task<BookmarkDto> CreateBookmarkAsync(Guid userId, CreateBookmarkRequest request)
    {
        var bookmark = new Bookmark
        {
            Id = Guid.NewGuid(),
            UserId = userId,
            Title = request.Title,
            Url = request.Url,
            Description = request.Description,
            Icon = request.Icon,
            Tags = request.Tags ?? Array.Empty<string>(),
            Visibility = request.Visibility,
            AllowedDevices = request.AllowedDevices,
            VisitCount = 0,
            Order = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
            CreatedAt = DateTime.UtcNow,
            UpdatedAt = DateTime.UtcNow
        };

        await _freeSql.Insert(bookmark).ExecuteAffrowsAsync();

        return MapToDto(bookmark);
    }

    /// <inheritdoc />
    public async Task<BookmarkDto?> UpdateBookmarkAsync(Guid bookmarkId, Guid userId, UpdateBookmarkRequest request)
    {
        var bookmark = await _freeSql.Select<Bookmark>()
            .Where(b => b.Id == bookmarkId && b.UserId == userId)
            .FirstAsync();

        if (bookmark == null)
        {
            return null;
        }

        if (!string.IsNullOrEmpty(request.Title))
        {
            bookmark.Title = request.Title;
        }

        if (!string.IsNullOrEmpty(request.Url))
        {
            bookmark.Url = request.Url;
        }

        if (request.Description != null)
        {
            bookmark.Description = request.Description;
        }

        if (request.Icon != null)
        {
            bookmark.Icon = request.Icon;
        }

        if (request.Tags != null)
        {
            bookmark.Tags = request.Tags;
        }

        if (request.Visibility.HasValue)
        {
            bookmark.Visibility = request.Visibility.Value;
        }

        if (request.AllowedDevices != null)
        {
            bookmark.AllowedDevices = request.AllowedDevices;
        }

        bookmark.UpdatedAt = DateTime.UtcNow;

        await _freeSql.Update<Bookmark>()
            .SetSource(bookmark)
            .ExecuteAffrowsAsync();

        return MapToDto(bookmark);
    }

    /// <inheritdoc />
    public async Task<bool> DeleteBookmarkAsync(Guid bookmarkId, Guid userId)
    {
        var affectedRows = await _freeSql.Delete<Bookmark>()
            .Where(b => b.Id == bookmarkId && b.UserId == userId)
            .ExecuteAffrowsAsync();

        return affectedRows > 0;
    }

    /// <inheritdoc />
    public async Task<int> BatchDeleteBookmarksAsync(Guid userId, Guid[] bookmarkIds)
    {
        var affectedRows = await _freeSql.Delete<Bookmark>()
            .Where(b => b.UserId == userId && bookmarkIds.Contains(b.Id))
            .ExecuteAffrowsAsync();

        return affectedRows;
    }

    /// <inheritdoc />
    public async Task<bool> SetVisibilityAsync(Guid bookmarkId, Guid userId, VisibilityType visibility, Guid[]? allowedDevices)
    {
        var affectedRows = await _freeSql.Update<Bookmark>()
            .Set(b => b.Visibility, visibility)
            .Set(b => b.AllowedDevices, allowedDevices)
            .Set(b => b.UpdatedAt, DateTime.UtcNow)
            .Where(b => b.Id == bookmarkId && b.UserId == userId)
            .ExecuteAffrowsAsync();

        return affectedRows > 0;
    }

    /// <inheritdoc />
    public async Task<bool> RecordVisitAsync(Guid bookmarkId, Guid userId)
    {
        var bookmark = await _freeSql.Select<Bookmark>()
            .Where(b => b.Id == bookmarkId && b.UserId == userId)
            .FirstAsync();

        if (bookmark == null)
        {
            return false;
        }

        await _freeSql.Update<Bookmark>()
            .Set(b => b.VisitCount, bookmark.VisitCount + 1)
            .Set(b => b.LastVisitTime, DateTime.UtcNow)
            .Where(b => b.Id == bookmarkId)
            .ExecuteAffrowsAsync();

        return true;
    }

    /// <inheritdoc />
    public async Task<bool> UpdateOrderAsync(Guid bookmarkId, Guid userId, long newOrder)
    {
        var affectedRows = await _freeSql.Update<Bookmark>()
            .Set(b => b.Order, newOrder)
            .Set(b => b.UpdatedAt, DateTime.UtcNow)
            .Where(b => b.Id == bookmarkId && b.UserId == userId)
            .ExecuteAffrowsAsync();

        return affectedRows > 0;
    }

    /// <inheritdoc />
    public async Task<bool> UrlExistsAsync(Guid userId, string url)
    {
        return await _freeSql.Select<Bookmark>()
            .Where(b => b.UserId == userId && b.Url == url)
            .AnyAsync();
    }

    /// <inheritdoc />
    public async Task<int> ImportBookmarksAsync(Guid userId, List<CreateBookmarkRequest> bookmarks)
    {
        var now = DateTime.UtcNow;
        var baseOrder = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        var importedCount = 0;

        foreach (var request in bookmarks)
        {
            // 检查 URL 是否已存在
            var exists = await UrlExistsAsync(userId, request.Url);
            if (exists)
            {
                continue;
            }

            var bookmark = new Bookmark
            {
                Id = Guid.NewGuid(),
                UserId = userId,
                Title = request.Title,
                Url = request.Url,
                Description = request.Description,
                Icon = request.Icon,
                Tags = request.Tags ?? Array.Empty<string>(),
                Visibility = request.Visibility,
                AllowedDevices = request.AllowedDevices,
                VisitCount = 0,
                Order = baseOrder + importedCount,
                CreatedAt = now,
                UpdatedAt = now
            };

            await _freeSql.Insert(bookmark).ExecuteAffrowsAsync();
            importedCount++;
        }

        return importedCount;
    }

    /// <inheritdoc />
    public async Task<List<BookmarkDto>> ExportBookmarksAsync(Guid userId)
    {
        var bookmarks = await _freeSql.Select<Bookmark>()
            .Where(b => b.UserId == userId)
            .OrderByDescending(b => b.Order)
            .ToListAsync();

        return bookmarks.Select(MapToDto).ToList();
    }

    private static BookmarkDto MapToDto(Bookmark bookmark)
    {
        return new BookmarkDto
        {
            Id = bookmark.Id,
            Title = bookmark.Title,
            Url = bookmark.Url,
            Description = bookmark.Description,
            Icon = bookmark.Icon,
            Tags = bookmark.Tags,
            VisitCount = bookmark.VisitCount,
            LastVisitTime = bookmark.LastVisitTime,
            Order = bookmark.Order,
            Visibility = bookmark.Visibility,
            AllowedDevices = bookmark.AllowedDevices,
            CreatedAt = bookmark.CreatedAt,
            UpdatedAt = bookmark.UpdatedAt
        };
    }
}

