using Microsoft.EntityFrameworkCore;
using JobTrackerApi.Data;
using JobTrackerApi.Models;

namespace JobTrackerApi.Services
{
    public sealed record FollowUpDecision(bool NeedsFollowUp, string? Reason, bool ShouldGhost);

    public static class RulesEngine
    {
        public static async Task<RuleSettings> GetSettings(JobTrackerContext db, CancellationToken cancellationToken)
        {
            if (!string.IsNullOrWhiteSpace(db.CurrentUserId))
            {
                var u = await db.UserRuleSettings
                    .AsNoTracking()
                    .FirstOrDefaultAsync(x => x.OwnerUserId == db.CurrentUserId, cancellationToken);

                if (u is not null)
                {
                    return new RuleSettings
                    {
                        Id = 1,
                        AppliedFollowUpDays = u.AppliedFollowUpDays,
                        AppliedGhostDays = u.AppliedGhostDays,
                        OfferFollowUpDays = u.OfferFollowUpDays,
                        OfferGhostDays = u.OfferGhostDays,
                        FeedbackFollowUpDays = u.FeedbackFollowUpDays,
                        FeedbackGhostDays = u.FeedbackGhostDays,
                    };
                }
            }

            var s = await db.RuleSettings.AsNoTracking().FirstOrDefaultAsync(x => x.Id == 1, cancellationToken);
            return s ?? new RuleSettings { Id = 1 };
        }

        public static FollowUpDecision Evaluate(
            RuleSettings s,
            JobApplication job,
            DateTime now,
            DateTime? lastMessageAt
        )
        {
            if (job.IsDeleted) return new FollowUpDecision(false, null, false);

            var status = job.Status ?? "Applied";
            if (status == "Interviewing") status = "Interview";

            // Nothing has been submitted in a pre-application stage, so there is nobody to chase
            // and nobody to be ghosted by. Guard before any date maths: a Saved job has no
            // DateApplied, and treating that as "very old" would silently auto-ghost it.
            if (JobPipeline.IsProspect(status)) return new FollowUpDecision(false, null, false);

            // Last activity: any explicit follow-up date, response date, feedback request, or correspondence message.
            var last = Max(
                job.DateApplied ?? job.SavedAt,
                job.ResponseDate,
                job.FollowUpAt,
                job.FeedbackRequestedAt,
                lastMessageAt
            );

            var daysSinceLast = (now - last).TotalDays;

            // Applied: if no response and enough time passed since applied.
            if (string.Equals(status, "Applied", StringComparison.OrdinalIgnoreCase) && !job.ResponseReceived)
            {
                // An Applied job should always have DateApplied. Fail safe rather than fall back to
                // a synthetic date, which could ghost the job on the next rules pass.
                if (job.DateApplied is null) return new FollowUpDecision(false, null, false);

                var daysSinceApplied = (now - job.DateApplied.Value).TotalDays;
                if (daysSinceApplied >= s.AppliedFollowUpDays)
                    return new FollowUpDecision(true, $"No reply after {s.AppliedFollowUpDays}d", daysSinceApplied >= s.AppliedGhostDays);
                return new FollowUpDecision(false, null, daysSinceApplied >= s.AppliedGhostDays);
            }

            // Offer/accepted waiting on next step
            if (string.Equals(status, "Offer", StringComparison.OrdinalIgnoreCase))
            {
                if (daysSinceLast >= s.OfferFollowUpDays)
                    return new FollowUpDecision(true, $"Stalled after {s.OfferFollowUpDays}d", daysSinceLast >= s.OfferGhostDays);
                return new FollowUpDecision(false, null, daysSinceLast >= s.OfferGhostDays);
            }

            // Rejected but feedback requested
            if (string.Equals(status, "Rejected", StringComparison.OrdinalIgnoreCase) && job.FeedbackRequestedAt is not null)
            {
                var daysSinceReq = (now - job.FeedbackRequestedAt.Value).TotalDays;
                if (daysSinceReq >= s.FeedbackFollowUpDays)
                    return new FollowUpDecision(true, $"Feedback requested {s.FeedbackFollowUpDays}d ago", daysSinceReq >= s.FeedbackGhostDays);
                return new FollowUpDecision(false, null, daysSinceReq >= s.FeedbackGhostDays);
            }

            // Waiting status: treat as follow-up based on applied follow-up days.
            if (string.Equals(status, "Waiting", StringComparison.OrdinalIgnoreCase))
            {
                if (daysSinceLast >= s.AppliedFollowUpDays)
                    return new FollowUpDecision(true, $"Waiting {s.AppliedFollowUpDays}d", daysSinceLast >= s.AppliedGhostDays);
                return new FollowUpDecision(false, null, daysSinceLast >= s.AppliedGhostDays);
            }

            // Default: no follow-up rule. Do not auto-ghost other statuses.
            return new FollowUpDecision(false, null, false);
        }

        public static DateTime Max(DateTime a, params DateTime?[] rest)
        {
            var m = a;
            foreach (var r in rest)
            {
                if (r is not null && r.Value > m) m = r.Value;
            }
            return m;
        }
    }
}

