using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using JobTrackerApi.Data;
using JobTrackerApi.Models;

namespace JobTrackerApi.Controllers
{
    [ApiController]
    // Explicitly authorized. These endpoints are all tenant-scoped user data, so they must not
    // depend on the Auth:Require fallback policy being switched on: a deployment that lost that flag
    // would otherwise serve them anonymously. docs/production-readiness-review.md.
    [Route("api/rules")]
    [Authorize(AuthenticationSchemes = "local")]
    public class RulesController : ControllerBase
    {
        private readonly JobTrackerContext _db;

        public RulesController(JobTrackerContext db)
        {
            _db = db;
        }

        [HttpGet]
        public async Task<ActionResult<RuleSettings>> Get(CancellationToken cancellationToken)
        {
            // Per-user rule settings when authenticated.
            if (!string.IsNullOrWhiteSpace(_db.CurrentUserId))
            {
                var u = await _db.UserRuleSettings.FirstOrDefaultAsync(x => x.OwnerUserId == _db.CurrentUserId, cancellationToken);
                if (u is null)
                {
                    u = new UserRuleSettings { OwnerUserId = _db.CurrentUserId };
                    _db.UserRuleSettings.Add(u);
                    await _db.SaveChangesAsync(cancellationToken);
                }

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

            // Fallback global settings.
            var s = await _db.RuleSettings.FirstOrDefaultAsync(x => x.Id == 1, cancellationToken);
            if (s is null)
            {
                s = new RuleSettings { Id = 1 };
                _db.RuleSettings.Add(s);
                await _db.SaveChangesAsync(cancellationToken);
            }
            return Ok(s);
        }

        [HttpPut]
        public async Task<IActionResult> Update([FromBody] RuleSettings incoming, CancellationToken cancellationToken)
        {
            // Per-user rule settings when authenticated.
            if (!string.IsNullOrWhiteSpace(_db.CurrentUserId))
            {
                var s = await _db.UserRuleSettings.FirstOrDefaultAsync(x => x.OwnerUserId == _db.CurrentUserId, cancellationToken);
                if (s is null)
                {
                    s = new UserRuleSettings { OwnerUserId = _db.CurrentUserId };
                    _db.UserRuleSettings.Add(s);
                }

                s.AppliedFollowUpDays = Clamp(incoming.AppliedFollowUpDays, 1, 365);
                s.AppliedGhostDays = Clamp(incoming.AppliedGhostDays, 1, 365);
                s.OfferFollowUpDays = Clamp(incoming.OfferFollowUpDays, 1, 365);
                s.OfferGhostDays = Clamp(incoming.OfferGhostDays, 1, 365);
                s.FeedbackFollowUpDays = Clamp(incoming.FeedbackFollowUpDays, 1, 365);
                s.FeedbackGhostDays = Clamp(incoming.FeedbackGhostDays, 1, 365);

                await _db.SaveChangesAsync(cancellationToken);
                return NoContent();
            }

            // Fallback global settings.
            var g = await _db.RuleSettings.FirstOrDefaultAsync(x => x.Id == 1, cancellationToken);
            if (g is null)
            {
                g = new RuleSettings { Id = 1 };
                _db.RuleSettings.Add(g);
            }

            g.AppliedFollowUpDays = Clamp(incoming.AppliedFollowUpDays, 1, 365);
            g.AppliedGhostDays = Clamp(incoming.AppliedGhostDays, 1, 365);
            g.OfferFollowUpDays = Clamp(incoming.OfferFollowUpDays, 1, 365);
            g.OfferGhostDays = Clamp(incoming.OfferGhostDays, 1, 365);
            g.FeedbackFollowUpDays = Clamp(incoming.FeedbackFollowUpDays, 1, 365);
            g.FeedbackGhostDays = Clamp(incoming.FeedbackGhostDays, 1, 365);

            await _db.SaveChangesAsync(cancellationToken);
            return NoContent();
        }

        private static int Clamp(int v, int min, int max) => v < min ? min : v > max ? max : v;
    }
}
