using System.Security.Claims;
using foodmarket.Application.Common.Email;
using foodmarket.Domain.Organizations;
using foodmarket.Domain.Platform;
using foodmarket.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace foodmarket.Api.Controllers.SuperAdmin;

/// <summary>SuperAdmin: единая платформенная настройка SMTP. GET отдаёт всё
/// КРОМЕ пароля (только has-password флаг). PUT принимает все поля + опционально
/// новый пароль. Пароль шифруется через DataProtection (purpose="foodmarket.smtp")
/// перед записью в БД. Все мутации пишутся в SuperAdminAuditLog с reason.</summary>
[ApiController]
[Authorize(Roles = "SuperAdmin")]
[Route("api/super-admin/platform-settings")]
public class PlatformSettingsController : ControllerBase
{
    private readonly AppDbContext _db;
    private readonly IDataProtectionProvider _dpProvider;
    private readonly IEmailSender _email;

    public PlatformSettingsController(AppDbContext db, IDataProtectionProvider dpProvider, IEmailSender email)
    {
        _db = db; _dpProvider = dpProvider; _email = email;
    }

    public record PlatformSettingsDto(
        string? SmtpHost, int? SmtpPort,
        bool SmtpUseSsl, bool SmtpStartTls,
        string? SmtpUsername, bool HasSmtpPassword,
        string? FromEmail, string? FromName,
        DateTime? UpdatedAt);

    public record PlatformSettingsInput(
        string Reason,
        string? SmtpHost, int? SmtpPort,
        bool SmtpUseSsl, bool SmtpStartTls,
        string? SmtpUsername,
        // Если null/пусто — пароль не меняется. Если задан — шифруется и
        // записывается. Чтобы СНЯТЬ пароль (отправлять без auth), используем
        // спец-значение "__clear__".
        string? NewSmtpPassword,
        string? FromEmail, string? FromName);

    public record TestSendInput(string ToEmail, string Subject, string Body);

    [HttpGet]
    public async Task<ActionResult<PlatformSettingsDto>> Get(CancellationToken ct)
    {
        var s = await GetOrCreateAsync(ct);
        return new PlatformSettingsDto(
            s.SmtpHost, s.SmtpPort,
            s.SmtpUseSsl, s.SmtpStartTls,
            s.SmtpUsername, !string.IsNullOrEmpty(s.SmtpPasswordEncrypted),
            s.FromEmail, s.FromName,
            s.UpdatedAt);
    }

    [HttpPut]
    public async Task<IActionResult> Update([FromBody] PlatformSettingsInput input, CancellationToken ct)
    {
        if (string.IsNullOrWhiteSpace(input.Reason) || input.Reason.Trim().Length < 10)
            return BadRequest(new { error = "Причина изменения обязательна (≥ 10 символов) — она пишется в журнал." });

        var s = await GetOrCreateAsync(ct);
        var prev = new
        {
            s.SmtpHost, s.SmtpPort, s.SmtpUseSsl, s.SmtpStartTls,
            s.SmtpUsername, s.FromEmail, s.FromName,
            HasPassword = !string.IsNullOrEmpty(s.SmtpPasswordEncrypted),
        };

        s.SmtpHost = input.SmtpHost?.Trim();
        s.SmtpPort = input.SmtpPort;
        s.SmtpUseSsl = input.SmtpUseSsl;
        s.SmtpStartTls = input.SmtpStartTls;
        s.SmtpUsername = input.SmtpUsername?.Trim();
        s.FromEmail = input.FromEmail?.Trim();
        s.FromName = input.FromName?.Trim();
        s.UpdatedAt = DateTime.UtcNow;

        if (input.NewSmtpPassword == "__clear__")
        {
            s.SmtpPasswordEncrypted = null;
        }
        else if (!string.IsNullOrEmpty(input.NewSmtpPassword))
        {
            var protector = _dpProvider.CreateProtector("foodmarket.smtp");
            s.SmtpPasswordEncrypted = protector.Protect(input.NewSmtpPassword);
        }

        await _db.SaveChangesAsync(ct);

        await LogAsync("PlatformSettingsUpdate",
            $"Обновлены SMTP-настройки платформы (host={s.SmtpHost} from={s.FromEmail})",
            input.Reason,
            System.Text.Json.JsonSerializer.Serialize(new { prev, next = new {
                s.SmtpHost, s.SmtpPort, s.SmtpUseSsl, s.SmtpStartTls,
                s.SmtpUsername, s.FromEmail, s.FromName,
                HasPassword = !string.IsNullOrEmpty(s.SmtpPasswordEncrypted),
            } }), ct);

        return NoContent();
    }

    [HttpPost("test-send")]
    public async Task<IActionResult> TestSend([FromBody] TestSendInput input, CancellationToken ct)
    {
        if (string.IsNullOrWhiteSpace(input.ToEmail))
            return BadRequest(new { error = "Адрес получателя обязателен." });
        try
        {
            await _email.SendAsync(input.ToEmail.Trim(),
                string.IsNullOrWhiteSpace(input.Subject) ? "Food Market — тестовое сообщение" : input.Subject,
                string.IsNullOrWhiteSpace(input.Body) ? "Тестовое письмо от Food Market." : input.Body,
                ct);
            return Ok(new { ok = true, sentTo = input.ToEmail });
        }
        catch (EmailNotConfiguredException ex)
        {
            return BadRequest(new { ok = false, error = ex.Message });
        }
        catch (Exception ex)
        {
            // Полный текст ошибки SMTP пишем в response — это SuperAdmin-only,
            // diagnostic-info ему нужна.
            return StatusCode(StatusCodes.Status500InternalServerError, new { ok = false, error = ex.Message });
        }
    }

    private async Task<PlatformSettings> GetOrCreateAsync(CancellationToken ct)
    {
        var s = await _db.PlatformSettings.FirstOrDefaultAsync(ct);
        if (s is null)
        {
            s = new PlatformSettings();
            _db.PlatformSettings.Add(s);
            await _db.SaveChangesAsync(ct);
        }
        return s;
    }

    private async Task LogAsync(string actionType, string description, string? reason, string changesJson, CancellationToken ct)
    {
        var userIdRaw = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub");
        Guid.TryParse(userIdRaw, out var uid);
        _db.SuperAdminAuditLogs.Add(new SuperAdminAuditLog
        {
            SuperAdminUserId = uid,
            ActionType = actionType,
            OrganizationId = null,
            Description = description, Reason = reason,
            ChangesJson = changesJson,
            IpAddress = HttpContext?.Connection?.RemoteIpAddress?.ToString() ?? "",
        });
        await _db.SaveChangesAsync(ct);
    }
}
