using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
using SqlSugar;
using XF.Domain.Constants;
using XF.Domain.Entities;
using XF.Domain.Extensions;
using XF.Domain.Repository;

namespace Hotline.Repository.SqlSugar.Ts;

public class BaseRepositoryTextSearch<TEntity> : IRepositoryTextSearch<TEntity>
    where TEntity : class, ITextSearch, new()
{
    private readonly ISugarUnitOfWork<HotlineDbContext> _uow;
    private readonly ISqlSugarClient _db;

    public BaseRepositoryTextSearch(ISugarUnitOfWork<HotlineDbContext> uow)
    {
        _uow = uow;
        _db = uow.Db;
    }

    /// <summary>
    /// 全都采用默认权重
    /// </summary>
    /// <param name="id">原始数据Id</param>
    /// <param name="creationTime">原始数据CreationTime</param>
    /// <param name="texts"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task AddVectorAsync(string id, DateTime creationTime, ICollection<string> texts, CancellationToken cancellationToken)
    {
        var weights = texts.Select(d => new NpgsqlWeight
        {
            Text = d
        }).ToList();

        var tableName = typeof(TEntity).Name.ToSnakeCase();
        var vectorSql = CreateVectorSql(weights);
        var vectorTexts = JsonSerializer.Serialize(texts, JsonDefaults.DefaultJsonSerializerOptions);
        var sql = $"INSERT INTO {tableName} (\"Id\",\"Vector\",\"VectorTexts\",\"CreationTime\") VALUES ('{id}', {vectorSql}, '{vectorTexts}'::json, '{creationTime}')";
        await _db.Ado.ExecuteCommandAsync(sql, null, cancellationToken);
        /*
        INSERT INTO "order_ts"
("Id","Vector","VectorTexts","CreationTime")
VALUES ('f595e730-909a-45e4-9138-a84bf15f4661', 
setweight(to_tsvector('simple', 'cc'), 'C') ||
setweight(to_tsvector('simple', 'bb'), 'C'),
'["cc","bb"]'
, '2023-09-06 10:51:39'
)
        */
    }

    /// <summary>
    /// 全都采用默认权重
    /// </summary>
    public async Task UpdateVectorAsync(string id, ICollection<string> texts, CancellationToken cancellationToken)
    {
        var weights = texts.Select(d => new NpgsqlWeight
        {
            Text = d
        }).ToList();

        var tableName = typeof(TEntity).Name.ToSnakeCase();
        var vectorSql = CreateVectorSql(weights);
        var vectorTexts = JsonSerializer.Serialize(texts, JsonDefaults.DefaultJsonSerializerOptions);
        var sql = $"UPDATE {tableName} SET \"Vector\"={vectorSql}, \"VectorTexts\"='{vectorTexts}'::json WHERE \"Id\"=@id ";
        await _db.Ado.ExecuteCommandAsync(sql, new { id }, cancellationToken);
    }

    public async Task<List<TEntity>> SearchAsync(IReadOnlyList<string> texts, CancellationToken cancellationToken)
    {
        var tableName = typeof(TEntity).Name.ToSnakeCase();
        var tsquery = string.Join('|', texts);
        var sql =
            $"SELECT \"Id\", \"Vector\", \"VectorTexts\", \"CreationTime\", ts_rank(\"Vector\", query) AS score FROM {tableName}, to_tsquery('simple', '{tsquery}') query WHERE \"Vector\" @@ query ORDER BY score DESC";
        return await _db.Ado.SqlQueryAsync<TEntity>(sql, null, cancellationToken);

        /*
         SELECT "Id", "Vector", "VectorTexts", "CreationTime", ts_rank("Vector", query) AS score 
FROM order_ts, to_tsquery('simple', 'bb') query WHERE "Vector" @@ query ORDER BY score DESC
         */
    }

    public async Task<List<TEntity>> SearchAsync(string id, CancellationToken cancellationToken)
    {
	    var tableName = typeof(TEntity).Name.ToSnakeCase();
	    var sql =
		    $"SELECT * FROM {tableName} WHERE \"Id\" ='{id}' ";
	    return await _db.Ado.SqlQueryAsync<TEntity>(sql, null, cancellationToken);

    }

	#region private method

	private static string CreateVectorSql(ICollection<NpgsqlWeight> weights)
    {
        var setweghtStrings = weights.Select(d => $"setweight(to_tsvector('simple', '{d.Text}'), '{d.Weight}')");
        var vectorString = string.Join(" || ", setweghtStrings);
        return vectorString;
    }

    #endregion
}