using CouponReport.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ClosedXML.Excel;
using iText.Kernel.Pdf;
using iText.Layout;
using iText.Layout.Element;
using iText.Layout.Properties;
using iText.Kernel.Font;
using iText.IO.Font;
using iText.IO.Font.Constants;
using CouponReport.Models.CouponMiddleware;
using CouponReport.Models.Parkingeyes;
using CouponReport.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.Cookies;

namespace CouponReport.Controllers;

[Authorize]
public class ReportController : Controller
{
    private readonly CouponMiddlewareContext _couponContext;
    private readonly ParkingEyesContext _parkingEyesContext;
    private readonly ReportService _reportService;

	public ReportController(CouponMiddlewareContext context, ParkingEyesContext parkingEyesContext, ReportService reportService)
    {
		_couponContext = context;
		_parkingEyesContext = parkingEyesContext;
		_reportService = reportService;
	}

    public async Task<IActionResult> Index(DateTime? startDate, DateTime? endDate)
    {
        // 如果沒有提供日期，預設為當月第一天到最後一天
        if (!startDate.HasValue && !endDate.HasValue)
        {
            var today = DateTime.Today;
            startDate = new DateTime(today.Year, today.Month, 1);
            endDate = startDate.Value.AddMonths(1).AddDays(-1);
        }

        return await LoadReportData(startDate, endDate);
    }

    [HttpPost]
    public async Task<IActionResult> Index(DateTime? startDate, DateTime? endDate, string action)
    {
        return await LoadReportData(startDate, endDate);
    }

    private async Task<IActionResult> LoadReportData(DateTime? startDate, DateTime? endDate)
    {
        var query = _couponContext.Logs.AsQueryable().Where(x => x.LogType == "Consume" && x.LogInfo.Contains("耗用成功"));
        var serialNo = query.GroupBy(x => x.SerialNo).Select(g => g.Key).ToList();
		var carEnter = _parkingEyesContext.CarEnters.Where(x => serialNo.Contains(x.SerialNo)).ToList();

		if (startDate.HasValue)
        {
            query = query.Where(x => x.LogTime >= startDate.Value);
        }

        if (endDate.HasValue)
        {
            query = query.Where(x => x.LogTime <= endDate.Value.AddDays(1).AddSeconds(-1));
        }

        var logs = await query.OrderBy(x => x.LogTime).ToListAsync();

        // 建立 SerialNo 到編號的映射
        var serialNoToRowNumber = new Dictionary<string, int>();
        int currentRowNumber = 1;

        // 建立 SerialNo 到 CarEnter 的映射，方便快速查找
        var carEnterDict = carEnter.GroupBy(x => x.SerialNo)
                                    .ToDictionary(g => g.Key, g => g.FirstOrDefault());

        var viewModel = new CouponReportViewModel
        {
            StartDate = startDate,
            EndDate = endDate,
            ReportItems = logs.Select(log =>
            {
                // 如果這個 SerialNo 還沒出現過，給它一個新編號
                if (!string.IsNullOrEmpty(log.SerialNo) && !serialNoToRowNumber.ContainsKey(log.SerialNo))
                {
                    serialNoToRowNumber[log.SerialNo] = currentRowNumber++;
                }

                // 從 CarEnter 取得出場時間
                DateTime? exitTime = null;
                var tenantCode = string.Empty;
				DateTime? invoiceDate = null;
                var invoiceNo = string.Empty;
				var invoiceAmount = 0m;

				if (!string.IsNullOrEmpty(log.SerialNo) && carEnterDict.ContainsKey(log.SerialNo))
                {
                    exitTime = carEnterDict[log.SerialNo]?.DepartureDateTime;
                }

                invoiceDate = _reportService.GetInvoiceDateTime(log.ExternalSystemKey);
				invoiceNo = _reportService.GetInvoiceNo(log.ExternalSystemKey);
				invoiceAmount = _reportService.GetInvoiceMoney(log.ExternalSystemKey);
                tenantCode = _reportService.GetTenantCode(log.ExternalSystemKey);

				return new CouponReportItem
                {
                    RowNumber = !string.IsNullOrEmpty(log.SerialNo) ? serialNoToRowNumber[log.SerialNo] : 0,
					TenantCode = tenantCode,
                    CarNumber = log.PlateNo,
                    InvoiceDate = invoiceDate,
                    InvoiceNumber = invoiceNo,
                    InvoiceAmount = invoiceAmount,
                    DiscountUnit = "新台幣",
                    DiscountAmount = log.DiscountAmount,
                    DiscountTime = log.LogTime,
                    EnterTime = log.EnterTime,
                    ExitTime = exitTime,
                    ParkingAmount = log.TotalAmount,
                    ClaimAmount = log.DiscountAmount
                };
            }).ToList()
        };

        return View(viewModel);
    }

    public async Task<IActionResult> ExportToExcel(DateTime? startDate, DateTime? endDate)
    {
		var query = _couponContext.Logs.AsQueryable().Where(x => x.LogType == "Consume" && x.LogInfo.Contains("耗用成功"));
		var serialNo = query.GroupBy(x => x.SerialNo).Select(g => g.Key).ToList();
		var carEnter = _parkingEyesContext.CarEnters.Where(x => serialNo.Contains(x.SerialNo)).ToList();

		if (startDate.HasValue)
        {
            query = query.Where(x => x.LogTime >= startDate.Value);
        }

        if (endDate.HasValue)
        {
            query = query.Where(x => x.LogTime <= endDate.Value.AddDays(1).AddSeconds(-1));
        }

        var logs = await query.OrderBy(x => x.LogTime).ToListAsync();

		// 建立 SerialNo 到編號的映射
		var serialNoToRowNumber = new Dictionary<string, int>();
		int currentRowNumber = 1;

		// 建立 SerialNo 到 CarEnter 的映射，方便快速查找
		var carEnterDict = carEnter.GroupBy(x => x.SerialNo)
									.ToDictionary(g => g.Key, g => g.FirstOrDefault());

		// 建立報表資料列表
		var reportItems = new List<(int rowNumber, string tenantCode, string carNumber, DateTime? invoiceDate,
									 string invoiceNo, decimal invoiceAmount, string discountUnit, decimal? discountAmount,
									 DateTime discountTime, DateTime? enterTime, DateTime? exitTime,
									 decimal? parkingAmount, decimal? claimAmount)>();

		foreach (var log in logs)
		{
			if (!string.IsNullOrEmpty(log.SerialNo) && !serialNoToRowNumber.ContainsKey(log.SerialNo))
			{
				serialNoToRowNumber[log.SerialNo] = currentRowNumber++;
			}

			DateTime? exitTime = null;
			if (!string.IsNullOrEmpty(log.SerialNo) && carEnterDict.ContainsKey(log.SerialNo))
			{
				exitTime = carEnterDict[log.SerialNo]?.DepartureDateTime;
			}

			var invoiceDate = _reportService.GetInvoiceDateTime(log.ExternalSystemKey);
			var invoiceNo = _reportService.GetInvoiceNo(log.ExternalSystemKey);
			var invoiceAmount = _reportService.GetInvoiceMoney(log.ExternalSystemKey);
			var tenantCode = _reportService.GetTenantCode(log.ExternalSystemKey);

			int rowNumber = !string.IsNullOrEmpty(log.SerialNo) ? serialNoToRowNumber[log.SerialNo] : 0;

			reportItems.Add((rowNumber, tenantCode, log.PlateNo, invoiceDate, invoiceNo, invoiceAmount,
							 "新台幣", log.DiscountAmount, log.LogTime, log.EnterTime, exitTime,
							 log.TotalAmount, log.DiscountAmount));
		}

		using var workbook = new XLWorkbook();
        var worksheet = workbook.Worksheets.Add("折扣報表");

        // 標題列
        worksheet.Cell(1, 1).Value = "編號";
        worksheet.Cell(1, 2).Value = "店別(統編)";
        worksheet.Cell(1, 3).Value = "車號";
        worksheet.Cell(1, 4).Value = "發票日期";
        worksheet.Cell(1, 5).Value = "發票號碼";
        worksheet.Cell(1, 6).Value = "發票金額";
        worksheet.Cell(1, 7).Value = "折扣單位";
        worksheet.Cell(1, 8).Value = "折扣金額";
        worksheet.Cell(1, 9).Value = "折扣時間";
        worksheet.Cell(1, 10).Value = "入場時間";
        worksheet.Cell(1, 11).Value = "出場時間";
        worksheet.Cell(1, 12).Value = "停車金額";
        worksheet.Cell(1, 13).Value = "請款金額";

        // 設定標題列樣式
        var headerRange = worksheet.Range(1, 1, 1, 13);
        headerRange.Style.Font.Bold = true;
        headerRange.Style.Fill.BackgroundColor = XLColor.LightGray;
        headerRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
        headerRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;

        // 填入資料並記錄合併範圍
        int row = 2;
        var mergedCells = new Dictionary<int, (int startRow, int endRow)>();

        foreach (var item in reportItems)
        {
            // 填入資料
            worksheet.Cell(row, 1).Value = item.rowNumber;
            worksheet.Cell(row, 2).Value = item.tenantCode;
            worksheet.Cell(row, 3).Value = item.carNumber;
            worksheet.Cell(row, 4).Value = item.invoiceDate?.ToString("yyyy-MM-dd HH:mm:ss");
            worksheet.Cell(row, 5).Value = item.invoiceNo;
            worksheet.Cell(row, 6).Value = item.invoiceAmount;
            worksheet.Cell(row, 7).Value = item.discountUnit;
            worksheet.Cell(row, 8).Value = item.discountAmount;
            worksheet.Cell(row, 9).Value = item.discountTime.ToString("yyyy-MM-dd HH:mm:ss");
            worksheet.Cell(row, 10).Value = item.enterTime?.ToString("yyyy-MM-dd HH:mm:ss");
            worksheet.Cell(row, 11).Value = item.exitTime?.ToString("yyyy-MM-dd HH:mm:ss");
            worksheet.Cell(row, 12).Value = item.parkingAmount;
            worksheet.Cell(row, 13).Value = item.claimAmount;

            // 記錄需要合併的儲存格範圍（按 rowNumber 分組）
            if (item.rowNumber > 0)
            {
                if (!mergedCells.ContainsKey(item.rowNumber))
                {
                    mergedCells[item.rowNumber] = (row, row);
                }
                else
                {
                    mergedCells[item.rowNumber] = (mergedCells[item.rowNumber].startRow, row);
                }
            }

            row++;
        }

        // 執行合併儲存格
        foreach (var (rowNumber, (startRow, endRow)) in mergedCells)
        {
            if (startRow < endRow)
            {
                // 合併：編號(1)、車號(3)、折扣單位(7)、折扣金額(8)、折扣時間(9)、入場時間(10)、出場時間(11)、停車金額(12)、請款金額(13)
                int[] mergeCols = { 1, 3, 7, 8, 9, 10, 11, 12, 13 };
                foreach (int col in mergeCols)
                {
                    var range = worksheet.Range(startRow, col, endRow, col);
                    range.Merge();
                    range.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
                    range.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
                }
            }
        }

        // 自動調整欄寬，並設定最小寬度以顯示完整表頭
        worksheet.Columns().AdjustToContents();
        for (int col = 1; col <= 13; col++)
        {
            if (worksheet.Column(col).Width < 12)
            {
                worksheet.Column(col).Width = 12;
            }
        }

        // 將所有資料儲存格置中
        var dataRange = worksheet.Range(2, 1, row - 1, 13);
        dataRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
        dataRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;

        var stream = new MemoryStream();
        workbook.SaveAs(stream);
        stream.Position = 0;

        var fileName = $"折扣報表_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
        return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
    }

    public async Task<IActionResult> ExportToPdf(DateTime? startDate, DateTime? endDate)
    {
		var query = _couponContext.Logs.AsQueryable().Where(x => x.LogType == "Consume" && x.LogInfo.Contains("耗用成功"));
		var serialNo = query.GroupBy(x => x.SerialNo).Select(g => g.Key).ToList();
		var carEnter = _parkingEyesContext.CarEnters.Where(x => serialNo.Contains(x.SerialNo)).ToList();

		if (startDate.HasValue)
        {
            query = query.Where(x => x.LogTime >= startDate.Value);
        }

        if (endDate.HasValue)
        {
            query = query.Where(x => x.LogTime <= endDate.Value.AddDays(1).AddSeconds(-1));
        }

        var logs = await query.OrderBy(x => x.LogTime).ToListAsync();

		// 建立 SerialNo 到編號的映射
		var serialNoToRowNumber = new Dictionary<string, int>();
		int currentRowNumber = 1;

		// 建立 SerialNo 到 CarEnter 的映射，方便快速查找
		var carEnterDict = carEnter.GroupBy(x => x.SerialNo)
									.ToDictionary(g => g.Key, g => g.FirstOrDefault());

		// 建立報表資料並分組
		var reportGroups = new Dictionary<int, List<(string tenantCode, DateTime? invoiceDate, string invoiceNo, decimal invoiceAmount)>>();
		var groupData = new Dictionary<int, (int rowNumber, string carNumber, string discountUnit, decimal? discountAmount,
											  DateTime discountTime, DateTime? enterTime, DateTime? exitTime,
											  decimal? parkingAmount, decimal? claimAmount)>();

		foreach (var log in logs)
		{
			if (!string.IsNullOrEmpty(log.SerialNo) && !serialNoToRowNumber.ContainsKey(log.SerialNo))
			{
				serialNoToRowNumber[log.SerialNo] = currentRowNumber++;
			}

			DateTime? exitTime = null;
			if (!string.IsNullOrEmpty(log.SerialNo) && carEnterDict.ContainsKey(log.SerialNo))
			{
				exitTime = carEnterDict[log.SerialNo]?.DepartureDateTime;
			}

			var invoiceDate = _reportService.GetInvoiceDateTime(log.ExternalSystemKey);
			var invoiceNo = _reportService.GetInvoiceNo(log.ExternalSystemKey);
			var invoiceAmount = _reportService.GetInvoiceMoney(log.ExternalSystemKey);
			var tenantCode = _reportService.GetTenantCode(log.ExternalSystemKey);

			int rowNumber = !string.IsNullOrEmpty(log.SerialNo) ? serialNoToRowNumber[log.SerialNo] : 0;

			// 記錄群組資料
			if (!groupData.ContainsKey(rowNumber))
			{
				groupData[rowNumber] = (rowNumber, log.PlateNo, "新台幣", log.DiscountAmount, log.LogTime,
										log.EnterTime, exitTime, log.TotalAmount, log.DiscountAmount);
				reportGroups[rowNumber] = new List<(string, DateTime?, string, decimal)>();
			}

			reportGroups[rowNumber].Add((tenantCode, invoiceDate, invoiceNo, invoiceAmount));
		}

        var stream = new MemoryStream();
        var writer = new PdfWriter(stream);
        writer.SetCloseStream(false); // 防止關閉 MemoryStream
        var pdf = new PdfDocument(writer);
        var document = new Document(pdf, iText.Kernel.Geom.PageSize.A4.Rotate());

        // 設定中文字型
        var fontPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "kaiu.ttf");
        if (!System.IO.File.Exists(fontPath))
        {
            fontPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "msjh.ttc,0");
        }

        PdfFont font;
        try
        {
            font = PdfFontFactory.CreateFont(fontPath, PdfEncodings.IDENTITY_H);
        }
        catch
        {
            font = PdfFontFactory.CreateFont(StandardFonts.HELVETICA);
        }

        document.SetFont(font);

        // 標題
        var title = new Paragraph("折扣報表")
            .SetTextAlignment(TextAlignment.CENTER)
            .SetFontSize(18);
        document.Add(title);

        // 建立表格
        var table = new Table(13).UseAllAvailableWidth();
        table.SetFontSize(8);

        // 標題列
        table.AddHeaderCell(new Cell().Add(new Paragraph("編號")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("店別")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("車號")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("發票日期")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("發票號碼")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("發票金額")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("折扣單位")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("折扣金額")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("折扣時間")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("入場時間")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("出場時間")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("停車金額")).SetTextAlignment(TextAlignment.CENTER));
        table.AddHeaderCell(new Cell().Add(new Paragraph("請款金額")).SetTextAlignment(TextAlignment.CENTER));

        // 資料列（使用合併儲存格）
        foreach (var (rowNum, group) in groupData.OrderBy(x => x.Key))
        {
            var invoices = reportGroups[rowNum];
            int rowSpan = invoices.Count;

            for (int i = 0; i < invoices.Count; i++)
            {
                var invoice = invoices[i];
                bool isFirstRow = i == 0;

                if (isFirstRow)
                {
                    // 第一行：顯示合併的欄位 - 編號
                    table.AddCell(new Cell(rowSpan, 1).Add(new Paragraph(group.rowNumber.ToString())).SetTextAlignment(TextAlignment.CENTER).SetVerticalAlignment(VerticalAlignment.MIDDLE));
                }

                // 每一行：店別
                table.AddCell(new Cell().Add(new Paragraph(invoice.tenantCode ?? "")).SetTextAlignment(TextAlignment.CENTER));

                if (isFirstRow)
                {
                    // 車號
                    table.AddCell(new Cell(rowSpan, 1).Add(new Paragraph(group.carNumber ?? "")).SetTextAlignment(TextAlignment.CENTER).SetVerticalAlignment(VerticalAlignment.MIDDLE));
                }

                // 每一行：發票日期、發票號碼、發票金額
                table.AddCell(new Cell().Add(new Paragraph(invoice.invoiceDate?.ToString("yyyy-MM-dd") ?? "")).SetTextAlignment(TextAlignment.CENTER));
                table.AddCell(new Cell().Add(new Paragraph(invoice.invoiceNo ?? "")).SetTextAlignment(TextAlignment.CENTER));
                table.AddCell(new Cell().Add(new Paragraph(invoice.invoiceAmount.ToString("N0"))).SetTextAlignment(TextAlignment.CENTER));

                if (isFirstRow)
                {
                    // 合併欄位：折扣單位、折扣金額、折扣時間、入場時間、出場時間、停車金額、請款金額
                    table.AddCell(new Cell(rowSpan, 1).Add(new Paragraph(group.discountUnit ?? "")).SetTextAlignment(TextAlignment.CENTER).SetVerticalAlignment(VerticalAlignment.MIDDLE));
                    table.AddCell(new Cell(rowSpan, 1).Add(new Paragraph(group.discountAmount?.ToString("N0") ?? "")).SetTextAlignment(TextAlignment.CENTER).SetVerticalAlignment(VerticalAlignment.MIDDLE));
                    table.AddCell(new Cell(rowSpan, 1).Add(new Paragraph(group.discountTime.ToString("yyyy-MM-dd HH:mm"))).SetTextAlignment(TextAlignment.CENTER).SetVerticalAlignment(VerticalAlignment.MIDDLE));
                    table.AddCell(new Cell(rowSpan, 1).Add(new Paragraph(group.enterTime?.ToString("yyyy-MM-dd HH:mm") ?? "")).SetTextAlignment(TextAlignment.CENTER).SetVerticalAlignment(VerticalAlignment.MIDDLE));
                    table.AddCell(new Cell(rowSpan, 1).Add(new Paragraph(group.exitTime?.ToString("yyyy-MM-dd HH:mm") ?? "")).SetTextAlignment(TextAlignment.CENTER).SetVerticalAlignment(VerticalAlignment.MIDDLE));
                    table.AddCell(new Cell(rowSpan, 1).Add(new Paragraph(group.parkingAmount?.ToString("N0") ?? "")).SetTextAlignment(TextAlignment.CENTER).SetVerticalAlignment(VerticalAlignment.MIDDLE));
                    table.AddCell(new Cell(rowSpan, 1).Add(new Paragraph(group.claimAmount?.ToString("N0") ?? "")).SetTextAlignment(TextAlignment.CENTER).SetVerticalAlignment(VerticalAlignment.MIDDLE));
                }
            }
        }

        document.Add(table);
        document.Close();

        stream.Position = 0;
        var fileName = $"折扣報表_{DateTime.Now:yyyyMMddHHmmss}.pdf";
        return File(stream, "application/pdf", fileName);
    }
}
