using System.Text.Json;
using HotelPosApi.Data;
using HotelPosApi.DTOs;
using HotelPosApi.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace HotelPosApi.Controllers;

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class BookingsController : ControllerBase
{
    private readonly AppDbContext _db;

    public BookingsController(AppDbContext db)
    {
        _db = db;
    }

    [HttpGet]
    public async Task<IActionResult> List()
    {
        var rows = await _db.Bookings
            .OrderBy(b => b.Id)
            .Select(b => new
            {
                id = b.BookingCode,
                guestName = b.GuestName,
                phone = b.Phone,
                email = b.Email,
                ic = b.Ic,
                nationality = b.Nationality,
                pricingId = b.PricingId,
                pax = DeserializeJson(b.PaxJson, new { adult = 1, kid = 0, elderly = 0 }),
                packageType = b.PackageType,
                course = b.Course,
                room = b.Room,
                checkin = b.Checkin.ToString("yyyy-MM-dd"),
                checkout = b.Checkout.ToString("yyyy-MM-dd"),
                nights = b.Nights,
                addons = DeserializeJson(b.AddonsJson, Array.Empty<object>()),
                fees = DeserializeJson(b.FeesJson, (object?)null),
                agentCode = b.AgentCode,
                agentId = b.AgentId,
                totalAmount = b.TotalAmount,
                amountPaid = b.AmountPaid,
                payMethod = b.PayMethod,
                remarks = b.Remarks,
                status = b.Status,
                createdAt = b.CreatedAtUtc.ToString("O"),
            })
            .ToListAsync();

        return Ok(rows);
    }

    [HttpPost]
    public async Task<IActionResult> Create([FromBody] BookingUpsertRequest req)
    {
        if (string.IsNullOrWhiteSpace(req.Id) ||
            string.IsNullOrWhiteSpace(req.GuestName) ||
            string.IsNullOrWhiteSpace(req.Room) ||
            string.IsNullOrWhiteSpace(req.Checkin) ||
            string.IsNullOrWhiteSpace(req.Checkout))
        {
            return BadRequest("Missing required booking fields.");
        }

        if (!DateOnly.TryParse(req.Checkin, out var checkin) ||
            !DateOnly.TryParse(req.Checkout, out var checkout) ||
            checkout <= checkin)
        {
            return BadRequest("Invalid check-in/check-out date.");
        }

        var exists = await _db.Bookings.AnyAsync(x => x.BookingCode == req.Id);
        if (exists)
            return Conflict("Booking already exists.");

        var entity = new Booking
        {
            BookingCode = req.Id.Trim(),
            GuestName = req.GuestName.Trim(),
            Phone = req.Phone?.Trim(),
            Email = req.Email?.Trim(),
            Ic = req.Ic?.Trim(),
            Nationality = string.IsNullOrWhiteSpace(req.Nationality) ? "Malaysian" : req.Nationality.Trim(),
            PricingId = req.PricingId,
            PackageType = req.PackageType?.Trim(),
            Course = req.Course?.Trim(),
            Room = req.Room.Trim(),
            Checkin = checkin,
            Checkout = checkout,
            Nights = Math.Max(1, req.Nights),
            PaxJson = SerializeJson(req.Pax, "{}"),
            AddonsJson = SerializeJson(req.Addons, "[]"),
            FeesJson = req.Fees == null ? null : SerializeJson(req.Fees, "{}"),
            AgentCode = req.AgentCode?.Trim(),
            AgentId = req.AgentId?.Trim(),
            TotalAmount = req.TotalAmount,
            AmountPaid = req.AmountPaid,
            PayMethod = string.IsNullOrWhiteSpace(req.PayMethod) ? "cash" : req.PayMethod.Trim(),
            Remarks = req.Remarks?.Trim(),
            Status = string.IsNullOrWhiteSpace(req.Status) ? "confirmed" : req.Status.Trim(),
            CreatedAtUtc = DateTime.UtcNow,
        };

        _db.Bookings.Add(entity);
        await _db.SaveChangesAsync();
        var attributeRows = BuildAttributeRows(req, entity.Id);
        if (attributeRows.Count > 0)
        {
            _db.BookingAttributes.AddRange(attributeRows);
            await _db.SaveChangesAsync();
        }

        return Ok(new { id = entity.BookingCode });
    }

    private static List<BookingAttribute> BuildAttributeRows(BookingUpsertRequest req, int bookingId)
    {
        var rows = new List<BookingAttribute>();
        AddAttribute(rows, bookingId, req.PricingId, "PackageType", req.PackageType);
        AddAttribute(rows, bookingId, req.PricingId, "Course", req.Course);
        AddAttribute(rows, bookingId, req.PricingId, "Room", req.Room);
        AddAttribute(rows, bookingId, req.PricingId, "Voucher", req.Voucher);
        AddAttribute(rows, bookingId, req.PricingId, "Deposit", req.Deposit?.ToString("0.00"));
        AddAttribute(rows, bookingId, req.PricingId, "PricingMeta", SerializeJson(req.PricingMeta, "{}"));
        AddAttribute(rows, bookingId, req.PricingId, "Pax", SerializeJson(req.Pax, "{}"));
        AddAttribute(rows, bookingId, req.PricingId, "Fees", SerializeJson(req.Fees, "{}"));
        AddAttribute(rows, bookingId, req.PricingId, "Addons", SerializeJson(req.Addons, "[]"));
        return rows;
    }

    private static void AddAttribute(List<BookingAttribute> rows, int bookingId, int? pricingId, string key, string? value)
    {
        if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(value))
            return;

        rows.Add(new BookingAttribute
        {
            BookingId = bookingId,
            PricingId = pricingId,
            AttributeKey = key.Trim(),
            AttributeValue = value.Trim(),
            CreatedAtUtc = DateTime.UtcNow,
        });
    }

    private static string SerializeJson(object? value, string fallback)
    {
        if (value == null) return fallback;
        try { return JsonSerializer.Serialize(value); }
        catch { return fallback; }
    }

    private static object? DeserializeJson(string? raw, object? fallback)
    {
        if (string.IsNullOrWhiteSpace(raw)) return fallback;
        try { return JsonSerializer.Deserialize<object>(raw); }
        catch { return fallback; }
    }
}
