using System.Text.Json;
using System.Text.RegularExpressions;
using Api.Clients.Api.Contracts;
using Api.Services.Contracts;
using CvMatcher.Models.Responses;
using CvSearch.Models.Data;
using CvSearch.Models.Data.Entities;
using CvSearch.Models.Settings;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace Api.Services;

public sealed class JobTokenService : IJobTokenService
{
    private readonly CvSearchDbContext _db;
    private readonly IRagApiClient _rag;
    private readonly JobSearchSettings _settings;
    private readonly ILogger<JobTokenService> _logger;

    public JobTokenService(
        CvSearchDbContext db,
        IRagApiClient rag,
        IOptions<JobSearchSettings> settings,
        ILogger<JobTokenService> logger)
    {
        _db = db;
        _rag = rag;
        _settings = settings.Value;
        _logger = logger;
    }

    public async Task<string> CreateTokenAsync(string cvDocumentId, string email, CancellationToken ct)
    {
        var token = new JobSearchTokenEntity
        {
            Id = Guid.NewGuid().ToString("N"),
            CvDocumentId = cvDocumentId,
            Email = email,
            ExpiresAt = DateTime.UtcNow.AddDays(_settings.TokenExpiryDays),
            Used = false,
            CreatedAt = DateTime.UtcNow
        };

        _db.JobSearchTokens.Add(token);
        await _db.SaveChangesAsync(ct);
        _logger.LogInformation("Job search token created. TokenId={TokenId}, CvDocumentId={CvDocumentId}", token.Id, cvDocumentId);
        return token.Id;
    }

    public async Task<string> TriggerStartAsync(string tokenId, CancellationToken ct)
    {
        var token = await _db.JobSearchTokens.FirstOrDefaultAsync(x => x.Id == tokenId, ct);
        if (token is null) return StartJobSearchStatus.NotFound;
        if (token.Used) return StartJobSearchStatus.AlreadyUsed;
        if (token.ExpiresAt <= DateTime.UtcNow) return StartJobSearchStatus.Expired;

        token.Used = true;
        await _db.SaveChangesAsync(ct);

        var cv = await _rag.GetDocumentAsync(token.CvDocumentId, ct);
        var keywords = cv is not null ? ExtractKeywords(cv.Text) : string.Empty;

        var providerConfigJson = JsonSerializer.Serialize(
            _settings.Providers.Where(p => p.Enabled).ToList(),
            new JsonSerializerOptions(JsonSerializerDefaults.Web));

        var session = new JobSearchSessionEntity
        {
            Id = Guid.NewGuid().ToString("N"),
            TokenId = token.Id,
            CvDocumentId = token.CvDocumentId,
            Email = token.Email,
            Status = JobSearchStatus.Pending,
            Keywords = keywords,
            ProviderConfigJson = providerConfigJson,
            CreatedAt = DateTime.UtcNow
        };

        _db.JobSearchSessions.Add(session);
        await _db.SaveChangesAsync(ct);
        _logger.LogInformation("Job search session created. SessionId={SessionId}, Keywords={Keywords}", session.Id, keywords);

        return StartJobSearchStatus.Started;
    }

    private static string ExtractKeywords(string cvText)
    {
        var lines = cvText
            .Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries)
            .Select(l => l.Trim())
            .Where(l => l.Length > 5 && l.Length < 200)
            .Where(l => !Regex.IsMatch(l, @"^[\d\s\+\-\(\)\@\.]+$"))
            .Take(5)
            .ToList();

        var words = lines
            .SelectMany(l => l.Split(' ', StringSplitOptions.RemoveEmptyEntries))
            .Select(w => Regex.Replace(w, @"[^\w\-]", ""))
            .Where(w => w.Length > 2)
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .Take(10)
            .ToList();

        return string.Join(",", words);
    }
}
