using Hangfire;
using Hangfire.Storage;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using QuantEngine.Web.Services;

namespace QuantEngine.Web.Pages.Admin.Operations;

[Authorize(AuthenticationSchemes = AdminAuthDefaults.Scheme)]
public class IndexModel : PageModel
{
    private readonly ILogger<IndexModel> _logger;

    public List<ScheduledJobInfo>? ScheduledJobs { get; set; }
    public List<JobExecutionInfo>? RecentExecutions { get; set; }
    public int TotalJobsCount { get; set; }
    public int ActiveJobsCount { get; set; }
    public int InactiveJobsCount { get; set; }
    public int PendingJobsCount { get; set; }
    public bool IsJobProcessorRunning { get; set; }
    public DateTime? LastRefreshTime { get; set; }
    public string? StatusMessage { get; set; }

    public IndexModel(ILogger<IndexModel> logger)
    {
        _logger = logger;
    }

    public Task OnGetAsync()
    {
        LoadOperationsData();
        return Task.CompletedTask;
    }

    public async Task<IActionResult> OnPostTriggerJobAsync(string jobId)
    {
        if (string.IsNullOrEmpty(jobId))
        {
            TempData["ErrorMessage"] = "올바르지 않은 작업 ID입니다.";
            return RedirectToPage();
        }

        try
        {
            RecurringJob.TriggerJob(jobId);
            TempData["SuccessMessage"] = $"작업 '{DescribeJobId(jobId)}'이(가) 즉시 실행 큐에 등록되었습니다.";
            _logger.LogInformation("Manually triggered Hangfire recurring job: {JobId}", jobId);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to trigger Hangfire job: {JobId}", jobId);
            TempData["ErrorMessage"] = $"작업 실행 실패: {ex.Message}";
        }

        return RedirectToPage();
    }

    private void LoadOperationsData()
    {
        try
        {
            LastRefreshTime = DateTime.UtcNow;

            // All data below comes directly from Hangfire's own storage
            // (JobStorage.Current), which already backs the real
            // recurring jobs registered in SchedulerService.InitializeSchedules
            // (daily-collection, hourly-price-update, weekly-report,
            // monthly-optimization) and every job it has actually run.
            // This page previously returned four fabricated job names and
            // fake execution timestamps with no connection to Hangfire at all.
            using IStorageConnection connection = JobStorage.Current.GetConnection();
            var monitoringApi = JobStorage.Current.GetMonitoringApi();

            var recurringJobs = connection.GetRecurringJobs();
            ScheduledJobs = recurringJobs
                .Select(j => new ScheduledJobInfo(
                    j.Id,
                    DescribeJobId(j.Id),
                    DescribeCron(j.Cron),
                    j.NextExecution,
                    string.IsNullOrEmpty(j.Error)))
                .ToList();

            TotalJobsCount = ScheduledJobs.Count;
            ActiveJobsCount = ScheduledJobs.Count(j => j.IsEnabled);
            InactiveJobsCount = TotalJobsCount - ActiveJobsCount;

            var succeeded = monitoringApi.SucceededJobs(0, 10)
                .Select(kv => {
                    var succeededAt = kv.Value.SucceededAt ?? DateTime.UtcNow;
                    var durationMs = kv.Value.TotalDuration ?? 0;
                    var startedAt = succeededAt.AddMilliseconds(-durationMs);
                    return new JobExecutionInfo(
                        kv.Value.Job?.Method.Name ?? kv.Key,
                        startedAt,
                        succeededAt,
                        true);
                });

            var failed = monitoringApi.FailedJobs(0, 10)
                .Select(kv => {
                    var failedAt = kv.Value.FailedAt ?? DateTime.UtcNow;
                    return new JobExecutionInfo(
                        kv.Value.Job?.Method.Name ?? kv.Key,
                        failedAt,
                        failedAt,
                        false);
                });

            RecentExecutions = succeeded.Concat(failed)
                .OrderByDescending(e => e.StartedAt)
                .Take(10)
                .ToList();

            var servers = monitoringApi.Servers();
            IsJobProcessorRunning = servers.Count > 0;
            PendingJobsCount = (int)monitoringApi.EnqueuedCount("default");

            StatusMessage = IsJobProcessorRunning
                ? $"{servers.Count}개 워커 서버 운영 중"
                : "Hangfire 서버가 등록되어 있지 않습니다";

            _logger.LogInformation("Operations data loaded from Hangfire ({Count} recurring jobs, {Servers} servers)",
                ScheduledJobs.Count, servers.Count);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to load operations data from Hangfire");
            ScheduledJobs = [];
            RecentExecutions = [];
            IsJobProcessorRunning = false;
            StatusMessage = "Hangfire 상태 조회 실패 - 로그 확인 필요";
        }
    }

    // Purely cosmetic: renders the actual CRON expression from Hangfire in
    // readable Korean where we recognize the exact pattern SchedulerService
    // registers, falling back to the raw CRON string for anything else so
    // this never hides or fabricates a schedule that isn't really there.
    private static string DescribeCron(string cron) => cron switch
    {
        "0 9 * * *" => "매일 09:00",
        "0 9,11,13,15 * * 1-5" => "평일 2시간 단위 (09:00~15:00)",
        "0 17 * * 5" => "매주 금요일 17:00",
        "0 2 1 * *" => "매월 1일 02:00",
        _ => cron,
    };

    // Same rationale as DescribeCron: display label for a real Hangfire
    // recurring-job id, not a substitute for it. Unknown ids pass through
    // unchanged.
    private static string DescribeJobId(string jobId) => jobId switch
    {
        "daily-collection" => "일일 데이터 수집",
        "hourly-price-update" => "시간별 가격 갱신",
        "weekly-report" => "주간 리포트 생성",
        "monthly-optimization" => "월간 최적화",
        _ => jobId,
    };
}

public record ScheduledJobInfo(string JobId, string JobName, string Schedule, DateTime? NextRun, bool IsEnabled);
public record JobExecutionInfo(string JobName, DateTime StartedAt, DateTime? CompletedAt, bool IsSuccess);
