using System.Security.Claims;
using JobTrackerApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace JobTrackerApi.Controllers;

/// <summary>
/// Outlook / Microsoft 365 connection lifecycle (connect, OAuth callback, status, disconnect).
/// Mirrors the Gmail OAuth surface in <see cref="GmailController"/>. Message search/import runs
/// through the provider-neutral <c>IEmailProvider</c> seam once a job's correspondence flow is
/// generalised past Gmail; this controller only owns the Microsoft-specific connection lifecycle.
/// </summary>
[ApiController]
[Route("api/microsoft-graph")]
[Authorize]
public sealed class MicrosoftGraphController : ControllerBase
{
    private readonly IMicrosoftGraphOAuthService _graph;
    private readonly ExternalOrigin _externalOrigin;

    public MicrosoftGraphController(IMicrosoftGraphOAuthService graph, IConfiguration cfg, ExternalOrigin? externalOrigin = null)
    {
        _graph = graph;
        _externalOrigin = externalOrigin ?? ExternalOrigin.FromConfiguration(cfg);
    }

    public sealed record MicrosoftGraphConnectionStatusDto(
        bool Connected,
        string? MailAddress,
        DateTimeOffset? ConnectedAt,
        DateTimeOffset? LastSyncedAt,
        DateTimeOffset? LastSyncAttemptedAt,
        DateTimeOffset? LastSyncSucceededAt,
        string? LastSyncMode,
        string? LastSyncSource,
        string? LastSyncStatus,
        string? LastSyncError);

    [HttpGet("status")]
    public async Task<ActionResult<MicrosoftGraphConnectionStatusDto>> Status(CancellationToken cancellationToken)
    {
        var ownerUserId = GetRequiredOwnerUserId();
        var connection = await _graph.GetConnectionAsync(ownerUserId, cancellationToken);
        return Ok(new MicrosoftGraphConnectionStatusDto(
            connection is not null,
            connection?.MailAddress,
            connection?.ConnectedAt,
            connection?.LastSyncedAt,
            connection?.LastSyncAttemptedAt,
            connection?.LastSyncSucceededAt,
            connection?.LastSyncMode,
            connection?.LastSyncSource,
            connection?.LastSyncStatus,
            connection?.LastSyncError));
    }

    [HttpGet("connect-url")]
    public IActionResult ConnectUrl()
    {
        var ownerUserId = GetRequiredOwnerUserId();
        var url = _graph.BuildAuthorizationUrl(ownerUserId, GetRedirectUri());
        return Ok(new { url });
    }

    [AllowAnonymous]
    [HttpGet("oauth/callback")]
    public async Task<IActionResult> Callback([FromQuery] string? code, [FromQuery] string? state, [FromQuery] string? error, CancellationToken cancellationToken)
    {
        if (!string.IsNullOrWhiteSpace(error))
        {
            return Content(BuildPopupHtml(false, $"Microsoft returned an error: {error}"), "text/html");
        }

        if (string.IsNullOrWhiteSpace(code) || string.IsNullOrWhiteSpace(state))
        {
            return Content(BuildPopupHtml(false, "Missing Microsoft OAuth code or state."), "text/html");
        }

        var ownerUserId = _graph.ConsumeState(state);
        if (string.IsNullOrWhiteSpace(ownerUserId))
        {
            return Content(BuildPopupHtml(false, "This Outlook connection request is no longer valid. Start the connection again."), "text/html");
        }

        try
        {
            var result = await _graph.ExchangeCodeAsync(ownerUserId, code, GetRedirectUri(), cancellationToken);
            return Content(BuildPopupHtml(true, $"Connected Outlook: {result.MailAddress}"), "text/html");
        }
        catch (Exception ex)
        {
            return Content(BuildPopupHtml(false, ex.Message), "text/html");
        }
    }

    [HttpDelete("connection")]
    public async Task<IActionResult> Disconnect(CancellationToken cancellationToken)
    {
        var ownerUserId = GetRequiredOwnerUserId();
        await _graph.DisconnectAsync(ownerUserId, cancellationToken);
        return NoContent();
    }

    private string GetRequiredOwnerUserId()
    {
        return User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub")
            ?? throw new InvalidOperationException("Authenticated user id is missing.");
    }

    private string GetRedirectUri()
    {
        return _externalOrigin.BuildPath("/api/microsoft-graph/oauth/callback");
    }

    private static string BuildPopupHtml(bool success, string message)
    {
        var escaped = System.Net.WebUtility.HtmlEncode(message);
        var status = success ? "connected" : "error";
        var title = success ? "Outlook connected" : "Outlook connection failed";
        var serializedMessage = System.Text.Json.JsonSerializer.Serialize(message);
        return $@"<!doctype html>
<html>
<head>
  <meta charset=""utf-8"" />
  <title>Outlook connection</title>
</head>
<body style=""font-family:Segoe UI,Arial,sans-serif;padding:24px;line-height:1.5;"">
  <h2>{title}</h2>
  <p>{escaped}</p>
  <p>You can close this window.</p>
  <script>
    if (window.opener) {{
      window.opener.postMessage({{ source: 'jobtracker-microsoft-oauth', status: '{status}', message: {serializedMessage} }}, '*');
    }}
    window.close();
  </script>
</body>
</html>";
    }
}
