using foodmarket.Api.Infrastructure.Tenancy;
using foodmarket.Application.Common.Tenancy;
using foodmarket.Infrastructure.Integrations.MoySklad;
using foodmarket.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace foodmarket.Api.Controllers.Admin;

[ApiController]
[Authorize(Policy = "AdminAccess")]
[Route("api/admin/moysklad")]
public class MoySkladImportController : ControllerBase
{
    private readonly IServiceScopeFactory _scopes;
    private readonly MoySkladImportService _svc;
    private readonly ImportJobRegistry _jobs;
    private readonly AppDbContext _db;
    private readonly ITenantContext _tenant;

    public MoySkladImportController(
        IServiceScopeFactory scopes,
        MoySkladImportService svc,
        ImportJobRegistry jobs,
        AppDbContext db,
        ITenantContext tenant)
    {
        _scopes = scopes;
        _svc = svc;
        _jobs = jobs;
        _db = db;
        _tenant = tenant;
    }

    public record TestRequest(string? Token = null);
    public record ImportRequest(string? Token = null, bool OverwriteExisting = false);
    public record SettingsDto(bool HasToken, string? Masked);
    public record SettingsInput(string Token);

    [HttpGet("settings")]
    public async Task<ActionResult<SettingsDto>> GetSettings(CancellationToken ct)
    {
        var token = await ReadTokenFromOrgAsync(ct);
        return new SettingsDto(!string.IsNullOrEmpty(token), Mask(token));
    }

    [HttpPut("settings")]
    public async Task<ActionResult<SettingsDto>> SetSettings([FromBody] SettingsInput input, CancellationToken ct)
    {
        var orgId = _tenant.OrganizationId ?? throw new InvalidOperationException("No tenant.");
        var org = await _db.Organizations.FirstOrDefaultAsync(o => o.Id == orgId, ct);
        if (org is null) return NotFound();
        org.MoySkladToken = string.IsNullOrWhiteSpace(input.Token) ? null : input.Token.Trim();
        await _db.SaveChangesAsync(ct);
        return new SettingsDto(!string.IsNullOrEmpty(org.MoySkladToken), Mask(org.MoySkladToken));
    }

    [HttpPost("test")]
    public async Task<IActionResult> TestConnection([FromBody] TestRequest req, CancellationToken ct)
    {
        var token = string.IsNullOrWhiteSpace(req.Token) ? await ReadTokenFromOrgAsync(ct) : req.Token;
        if (string.IsNullOrWhiteSpace(token))
            return BadRequest(new { error = "Token is required." });

        var result = await _svc.TestConnectionAsync(token, ct);
        if (!result.Success)
        {
            var msg = result.StatusCode switch
            {
                401 or 403 => "Токен недействителен или не имеет доступа к API.",
                503 or 502 => "МойСклад временно недоступен. Повтори через минуту.",
                _ => $"МойСклад вернул {result.StatusCode}: {Truncate(result.Error, 400)}",
            };
            return StatusCode(result.StatusCode ?? 502, new { error = msg });
        }
        return Ok(new { organization = result.Value!.Name, inn = result.Value.Inn });
    }

    // Async launch — возвращает jobId, реальная работа в фоне. Клиент полит /jobs/{id}.

    [HttpPost("import-products")]
    public async Task<ActionResult<object>> ImportProducts([FromBody] ImportRequest req, CancellationToken ct)
    {
        var token = string.IsNullOrWhiteSpace(req.Token) ? await ReadTokenFromOrgAsync(ct) : req.Token;
        if (string.IsNullOrWhiteSpace(token))
            return BadRequest(new { error = "Токен не настроен. Сохраните его в /admin/moysklad/settings." });
        var orgId = _tenant.OrganizationId ?? throw new InvalidOperationException("No tenant.");

        var job = _jobs.Create("products");
        job.Stage = "Подключение к MoySklad…";
        _ = RunInBackgroundAsync(job, async (svc, progress, ctInner) =>
        {
            progress.Stage = "Импорт товаров…";
            var result = await svc.ImportProductsAsync(token, req.OverwriteExisting, ctInner, progress);
            progress.Message = $"Готово: {result.Created} записей (создано/обновлено), {result.Skipped} пропущено, {result.GroupsCreated} групп.";
        }, orgId);
        return Ok(new { jobId = job.Id });
    }

    [HttpPost("import-counterparties")]
    public async Task<ActionResult<object>> ImportCounterparties([FromBody] ImportRequest req, CancellationToken ct)
    {
        var token = string.IsNullOrWhiteSpace(req.Token) ? await ReadTokenFromOrgAsync(ct) : req.Token;
        if (string.IsNullOrWhiteSpace(token))
            return BadRequest(new { error = "Токен не настроен. Сохраните его в /admin/moysklad/settings." });
        var orgId = _tenant.OrganizationId ?? throw new InvalidOperationException("No tenant.");

        var job = _jobs.Create("counterparties");
        job.Stage = "Подключение к MoySklad…";
        _ = RunInBackgroundAsync(job, async (svc, progress, ctInner) =>
        {
            progress.Stage = "Импорт контрагентов…";
            var result = await svc.ImportCounterpartiesAsync(token, req.OverwriteExisting, ctInner, progress);
            progress.Message = $"Готово: {result.Created} записей, {result.Skipped} пропущено.";
        }, orgId);
        return Ok(new { jobId = job.Id });
    }

    private Task RunInBackgroundAsync(
        ImportJobProgress job,
        Func<MoySkladImportService, ImportJobProgress, CancellationToken, Task> work,
        Guid orgId)
    {
        return Task.Run(async () =>
        {
            try
            {
                using var tenantScope = HttpContextTenantContext.UseOverride(orgId);
                using var scope = _scopes.CreateScope();
                var svc = scope.ServiceProvider.GetRequiredService<MoySkladImportService>();
                await work(svc, job, CancellationToken.None);
                job.Status = ImportJobStatus.Succeeded;
            }
            catch (Exception ex)
            {
                job.Status = ImportJobStatus.Failed;
                job.Message = ex.Message;
                job.Errors.Add(ex.ToString());
            }
            finally
            {
                job.FinishedAt = DateTime.UtcNow;
            }
        });
    }

    private async Task<string?> ReadTokenFromOrgAsync(CancellationToken ct)
    {
        var orgId = _tenant.OrganizationId;
        if (orgId is null) return null;
        return await _db.Organizations
            .Where(o => o.Id == orgId)
            .Select(o => o.MoySkladToken)
            .FirstOrDefaultAsync(ct);
    }

    private static string? Mask(string? token)
    {
        if (string.IsNullOrEmpty(token)) return null;
        if (token.Length <= 8) return new string('•', token.Length);
        return token[..4] + new string('•', 8) + token[^4..];
    }

    private static string? Truncate(string? s, int max)
        => string.IsNullOrEmpty(s) ? s : (s.Length <= max ? s : s[..max] + "…");
}
