using System.Runtime.InteropServices;
using JobTrackerApi.Data;
using JobTrackerApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace JobTrackerApi.Controllers;

[ApiController]
[Route("api/admin/system")]
[Authorize(Roles = "Admin")]
public sealed class AdminSystemController : ControllerBase
{
    private readonly IConfiguration _cfg;
    private readonly AppPaths _paths;
    private readonly JobTrackerContext _db;
    private readonly ISummarizerService _summarizer;
    private readonly IWebHostEnvironment _env;
    private readonly IEmailSettingsResolver _emailSettings;

    public AdminSystemController(IConfiguration cfg, AppPaths paths, JobTrackerContext db, ISummarizerService summarizer, IWebHostEnvironment env, IEmailSettingsResolver emailSettings)
    {
        _cfg = cfg;
        _paths = paths;
        _db = db;
        _summarizer = summarizer;
        _env = env;
        _emailSettings = emailSettings;
    }

    public sealed record StorageStatusDto(string DataRoot, string DbPath, bool DbExists, long? DbSizeBytes, int CompanyCount, int JobCount, int DeletedCount);
    public sealed record EmailStatusDto(bool Enabled, string? Host, int Port, bool EnableSsl, string? From, string? FromName);
    public sealed record DatabaseStatusDto(string Provider, bool LooksConfigured, bool CanConnect, string? Target, bool UsesFileStorage, string? Warning);
    public sealed record RuntimeStatusDto(string Framework, string OSDescription, string ProcessArchitecture, string? MachineName);
    public sealed record AuthStatusDto(bool Required, bool HasJwtKey, bool GoogleConfigured, bool GmailConfigured);
    public sealed record CvBenchmarkStatusDto(string? IndexJson, string? ReportMarkdown, string RootPath, DateTimeOffset? LastUpdatedAtUtc);
    public sealed record SystemStatusDto(
        string Environment,
        string ContentRoot,
        string Version,
        string? CommitSha,
        string? BuildStamp,
        StorageStatusDto Storage,
        EmailStatusDto Email,
        DatabaseStatusDto Database,
        RuntimeStatusDto Runtime,
        AuthStatusDto Auth,
        AiServiceMetrics Ai
    );

    // Shared with the /health endpoint so both report the same version from the same key.
    private static string? NormalizeBuildMetadata(string? value) => BuildMetadata.Normalize(value);

    private EmailSettingsSnapshot BuildFallbackEmailSettingsSnapshot()
    {
        var host = (_cfg["Email:SmtpHost"] ?? string.Empty).Trim();
        var user = (_cfg["Email:SmtpUser"] ?? string.Empty).Trim();
        var password = (_cfg["Email:SmtpPassword"] ?? string.Empty).Trim();
        var from = (_cfg["Email:From"] ?? user).Trim();
        var fromName = (_cfg["Email:FromName"] ?? "Jobbjakt").Trim();
        var port = _cfg.GetValue("Email:SmtpPort", 587);
        if (port <= 0) port = 587;
        var enableSsl = _cfg.GetValue("Email:SmtpEnableSsl", true);
        var timeoutMs = _cfg.GetValue("Email:SmtpTimeoutMs", 15000);
        if (timeoutMs <= 0) timeoutMs = 15000;
        var enabled = _cfg.GetValue("Email:Enabled", false);

        return new EmailSettingsSnapshot(
            Enabled: enabled,
            Host: host,
            Port: port,
            User: user,
            Password: password,
            From: from,
            FromName: fromName,
            EnableSsl: enableSsl,
            TimeoutMs: timeoutMs,
            UsesOverrides: false,
            HasPassword: !string.IsNullOrWhiteSpace(password));
    }

    private EmailSettingsAdminDto BuildFallbackEmailSettings(string? reason = null)
    {
        var snapshot = BuildFallbackEmailSettingsSnapshot();
        return new EmailSettingsAdminDto(
            Enabled: snapshot.Enabled,
            Host: snapshot.Host,
            Port: snapshot.Port,
            User: snapshot.User,
            From: snapshot.From,
            FromName: string.IsNullOrWhiteSpace(reason) ? snapshot.FromName : $"{snapshot.FromName} (fallback)",
            EnableSsl: snapshot.EnableSsl,
            TimeoutMs: snapshot.TimeoutMs,
            UsesOverrides: snapshot.UsesOverrides,
            HasPassword: snapshot.HasPassword);
    }

    [HttpPost("ai/probe")]
    [HttpPost("summarizer/probe")]
    public async Task<IActionResult> RunSummarizerProbe(CancellationToken cancellationToken)
    {
        await _summarizer.RunProbeAsync(cancellationToken);
        return NoContent();
    }

    [HttpGet("email-settings")]
    public async Task<ActionResult<EmailSettingsAdminDto>> GetEmailSettings(CancellationToken cancellationToken)
    {
        try
        {
            return Ok(await _emailSettings.GetAdminDtoAsync(cancellationToken));
        }
        catch (Exception ex)
        {
            return Ok(BuildFallbackEmailSettings(ex.Message));
        }
    }

    [HttpPut("email-settings")]
    public async Task<ActionResult<EmailSettingsAdminDto>> UpdateEmailSettings([FromBody] UpdateEmailSettingsRequest request, CancellationToken cancellationToken)
    {
        if (request.Port <= 0) return BadRequest("SMTP port must be greater than 0.");
        if (request.TimeoutMs <= 0) return BadRequest("SMTP timeout must be greater than 0.");
        return Ok(await _emailSettings.UpdateAsync(request, cancellationToken));
    }

    [HttpGet("cv-benchmark")]
    public async Task<ActionResult<CvBenchmarkStatusDto>> GetCvBenchmarkStatus(CancellationToken cancellationToken)
    {
        var indexPath = Path.Combine(_paths.CvBenchmarksRoot, "index.json");
        var reportPath = Path.Combine(_paths.CvBenchmarksRoot, "report.md");
        var indexJson = System.IO.File.Exists(indexPath) ? await System.IO.File.ReadAllTextAsync(indexPath, cancellationToken) : null;
        var reportMarkdown = System.IO.File.Exists(reportPath) ? await System.IO.File.ReadAllTextAsync(reportPath, cancellationToken) : null;
        var lastUpdated = new[]
        {
            System.IO.File.Exists(indexPath) ? System.IO.File.GetLastWriteTimeUtc(indexPath) : (DateTime?)null,
            System.IO.File.Exists(reportPath) ? System.IO.File.GetLastWriteTimeUtc(reportPath) : (DateTime?)null,
        }.Where(value => value.HasValue).Select(value => value!.Value).DefaultIfEmpty().Max();

        return Ok(new CvBenchmarkStatusDto(indexJson, reportMarkdown, _paths.CvBenchmarksRoot, lastUpdated == default ? null : new DateTimeOffset(DateTime.SpecifyKind(lastUpdated, DateTimeKind.Utc))));
    }

    [HttpGet]
    public async Task<ActionResult<SystemStatusDto>> Get(CancellationToken cancellationToken)
    {
        var provider = (_cfg["Database:Provider"] ?? "sqlite").Trim().ToLowerInvariant();
        var usesFileStorage = provider is not "mysql" and not "mariadb";
        var dbPath = _paths.GetDbPath();
        var dbFile = new FileInfo(dbPath);

        var jobCount = 0;
        var deletedCount = 0;
        var companies = 0;
        string? statusWarning = null;
        try
        {
            jobCount = await _db.JobApplications.AsNoTracking().CountAsync(cancellationToken);
            deletedCount = await _db.JobApplications.AsNoTracking().CountAsync(x => x.IsDeleted, cancellationToken);
            companies = await _db.Companies.AsNoTracking().CountAsync(cancellationToken);
        }
        catch (Exception ex)
        {
            statusWarning = $"Data query failed: {ex.GetType().Name}";
        }

        AiServiceMetrics ai;
        try
        {
            ai = await _summarizer.GetMetricsAsync(cancellationToken);
        }
        catch (Exception ex)
        {
            ai = new AiServiceMetrics(
                Healthy: false,
                Model: null,
                Device: null,
                GpuAvailable: false,
                GpuName: null,
                OcrAvailable: false,
                OcrLanguages: null,
                OllamaConfigured: null,
                OllamaReachable: null,
                OllamaModel: null,
                OllamaModelAvailable: null,
                OllamaVersion: null,
                OllamaInstalledModels: Array.Empty<string>(),
                OllamaLoadedModels: Array.Empty<string>(),
                OllamaLoadedCount: 0,
                HealthLatencyMs: null,
                ProbeLatencyMs: null,
                LastProbeAt: null,
                LastProbeSuccessAt: null,
                LastProbeFailureAt: null,
                ProbeFailures: 0,
                Requests: 0,
                CacheHits: 0,
                CacheMisses: 0,
                Failures: 0,
                AverageLatencyMs: null,
                OcrRequests: 0,
                OcrFailures: 0,
                AverageOcrLatencyMs: null,
                LastOcrSuccessAt: null,
                LastOcrFailureAt: null,
                LastSuccessAt: null,
                LastFailureAt: null,
                LastError: ex.Message);
        }

        var version = BuildMetadata.ResolveVersion(_cfg);

        var commitSha = NormalizeBuildMetadata(_cfg["App:CommitSha"]);
        var buildStamp = NormalizeBuildMetadata(_cfg["App:BuildStamp"]);
        var connectionString = _cfg.GetConnectionString("JobTracker") ?? string.Empty;
        var looksConfigured = !string.IsNullOrWhiteSpace(connectionString) || usesFileStorage;

        string? dbTarget;
        if (usesFileStorage)
        {
            dbTarget = dbPath;
        }
        else if (string.IsNullOrWhiteSpace(connectionString))
        {
            dbTarget = null;
        }
        else
        {
            dbTarget = connectionString.Split(';', StringSplitOptions.RemoveEmptyEntries)
                .Select(part => part.Trim())
                .FirstOrDefault(part => part.StartsWith("server=", StringComparison.OrdinalIgnoreCase)
                    || part.StartsWith("host=", StringComparison.OrdinalIgnoreCase)
                    || part.StartsWith("database=", StringComparison.OrdinalIgnoreCase));
        }

        bool canConnect;
        try
        {
            canConnect = await _db.Database.CanConnectAsync(cancellationToken);
        }
        catch
        {
            canConnect = false;
        }

        string? dbWarning = null;
        if (!looksConfigured)
        {
            dbWarning = "Connection string is missing.";
        }
        else if (!canConnect)
        {
            dbWarning = "Database connection failed.";
        }
        else if (!usesFileStorage && jobCount == 0 && companies == 0)
        {
            dbWarning = "Connected, but no data is present yet. Check whether this is the intended production database.";
        }

        if (!string.IsNullOrWhiteSpace(statusWarning))
        {
            dbWarning = string.IsNullOrWhiteSpace(dbWarning)
                ? statusWarning
                : $"{dbWarning} {statusWarning}";
        }

        var gmailConfigured = !string.IsNullOrWhiteSpace((_cfg["Google:GmailClientSecret"] ?? string.Empty).Trim());
        EmailSettingsSnapshot emailSettings;
        try
        {
            emailSettings = await _emailSettings.GetSnapshotAsync(cancellationToken);
        }
        catch (Exception)
        {
            emailSettings = BuildFallbackEmailSettingsSnapshot();
        }

        return Ok(new SystemStatusDto(
            Environment: _env.EnvironmentName,
            ContentRoot: _env.ContentRootPath,
            Version: version,
            CommitSha: string.IsNullOrWhiteSpace(commitSha) ? null : commitSha,
            BuildStamp: string.IsNullOrWhiteSpace(buildStamp) ? null : buildStamp,
            Storage: new StorageStatusDto(
                DataRoot: _paths.DataRoot,
                DbPath: dbPath,
                DbExists: dbFile.Exists,
                DbSizeBytes: dbFile.Exists ? dbFile.Length : null,
                CompanyCount: companies,
                JobCount: jobCount,
                DeletedCount: deletedCount
            ),
            Email: new EmailStatusDto(
                Enabled: emailSettings.Enabled,
                Host: emailSettings.Host,
                Port: emailSettings.Port,
                EnableSsl: emailSettings.EnableSsl,
                From: emailSettings.From,
                FromName: emailSettings.FromName
            ),
            Database: new DatabaseStatusDto(
                Provider: provider,
                LooksConfigured: looksConfigured,
                CanConnect: canConnect,
                Target: dbTarget,
                UsesFileStorage: usesFileStorage,
                Warning: dbWarning
            ),
            Runtime: new RuntimeStatusDto(
                Framework: RuntimeInformation.FrameworkDescription,
                OSDescription: RuntimeInformation.OSDescription,
                ProcessArchitecture: RuntimeInformation.ProcessArchitecture.ToString(),
                MachineName: Environment.MachineName
            ),
            Auth: new AuthStatusDto(
                Required: _cfg.GetValue("Auth:Require", false),
                HasJwtKey: !string.IsNullOrWhiteSpace((_cfg["Auth:JwtKey"] ?? string.Empty).Trim()),
                GoogleConfigured: !string.IsNullOrWhiteSpace((_cfg["Auth:GoogleClientId"] ?? string.Empty).Trim()),
                GmailConfigured: gmailConfigured
            ),
            Ai: ai
        ));
    }
}
