using Microsoft.AspNetCore.Mvc;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using eSPJ.Models;
using eSPJ.Services;

namespace eSPJ.Controllers.SpjDriverUpstController
{
    [Route("upst/detail-penjemputan")]
    public class DetailPenjemputanController : Controller
    {
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly IConfiguration _configuration;
        private readonly DetailPenjemputanService _detailService;
        private readonly ILogger<DetailPenjemputanController> _logger;
        private readonly IWebHostEnvironment _env;

        public DetailPenjemputanController(
            IHttpClientFactory httpClientFactory, 
            IConfiguration configuration,
            DetailPenjemputanService detailService,
            ILogger<DetailPenjemputanController> logger,
            IWebHostEnvironment env)
        {
            _httpClientFactory = httpClientFactory;
            _configuration = configuration;
            _detailService = detailService;
            _logger = logger;
            _env = env;
        }

        private static string SanitizePathSegment(string? value, string fallback = "umum")
        {
            var safe = string.Concat((value ?? string.Empty).Trim().Select(c =>
                char.IsLetterOrDigit(c) || c == '-' || c == '_'
                    ? c
                    : '-'));

            safe = Regex.Replace(safe, "-+", "-").Trim('-');
            return string.IsNullOrWhiteSpace(safe) ? fallback : safe;
        }

        private string GetUploadDirectory(string dateFolder, string? nomorSpj = null, string? namaTps = null)
        {
            var uploadDir = Path.Combine(
                _env.ContentRootPath,
                "uploads",
                "penjemputan",
                dateFolder,
                SanitizePathSegment(nomorSpj, "spj-umum"),
                SanitizePathSegment(namaTps, "tps-1"));
            Directory.CreateDirectory(uploadDir);
            return uploadDir;
        }

        private static string BuildUploadUrl(string dateFolder, string fileName, string? nomorSpj = null, string? namaTps = null)
        {
            var spjFolder = SanitizePathSegment(nomorSpj, "spj-umum");
            var tpsFolder = SanitizePathSegment(namaTps, "tps-1");
            return $"/uploads/penjemputan/{dateFolder}/{spjFolder}/{tpsFolder}/{fileName}";
        }

        private async Task<TpsData?> FindExistingRecordAsync(string? nomorSpj, string? spjDetailId, string? lokasiAngkutId, string? namaTps)
        {
            if (string.IsNullOrWhiteSpace(nomorSpj))
            {
                return null;
            }

            return await _detailService.GetRecordDetailAsync(nomorSpj, spjDetailId, lokasiAngkutId, namaTps);
        }

        private static List<RecordTimbanganItem> MapRecordTimbangan(List<TimbanganItem>? items)
        {
            return (items ?? new List<TimbanganItem>())
                .Select(item => new RecordTimbanganItem
                {
                    Berat = item.Berat?.FirstOrDefault() ?? 0,
                    JenisSampah = item.JenisSampah != null && item.JenisSampah.Count > 0
                        ? item.JenisSampah[0].ToString()
                        : "Residu",
                    FotoFileName = item.FotoFileName ?? string.Empty,
                    Uploaded = item.IsUploaded,
                    OcrInfo = item.IsUploaded ? "Foto dari server." : "OCR: belum diproses."
                })
                .ToList();
        }

        private static RecordSaveRequest BuildRecordSaveRequest(TpsData? existingRecord, string? nomorSpj, string? namaTps, string? spjDetailId, string? lokasiAngkutId)
        {
            return new RecordSaveRequest
            {
                NomorSpj = nomorSpj ?? existingRecord?.NomorSpj ?? string.Empty,
                NamaTps = namaTps ?? existingRecord?.Name ?? string.Empty,
                SpjDetailId = spjDetailId ?? existingRecord?.SpjDetailId ?? string.Empty,
                LokasiAngkutId = lokasiAngkutId ?? existingRecord?.LokasiAngkutId ?? string.Empty,
                Latitude = existingRecord?.Latitude ?? string.Empty,
                Longitude = existingRecord?.Longitude ?? string.Empty,
                AlamatJalan = existingRecord?.AlamatJalan ?? string.Empty,
                WaktuKedatangan = existingRecord?.WaktuKedatangan ?? string.Empty,
                FotoKedatanganFileNames = existingRecord?.FotoKedatangan != null ? new List<string>(existingRecord.FotoKedatangan) : new List<string>(),
                FotoKedatanganUploaded = existingRecord?.FotoKedatanganUploaded ?? false,
                Timbangan = MapRecordTimbangan(existingRecord?.Timbangan),
                TotalOrganik = existingRecord?.TotalOrganik ?? 0,
                TotalAnorganik = existingRecord?.TotalAnorganik ?? 0,
                TotalResidu = existingRecord?.TotalResidu ?? 0,
                TotalTimbangan = existingRecord?.TotalTimbangan ?? 0,
                FotoPetugasFileNames = existingRecord?.FotoPetugas != null ? new List<string>(existingRecord.FotoPetugas) : new List<string>(),
                FotoPetugasUploaded = existingRecord?.FotoPetugasUploaded ?? false,
                NamaPetugas = existingRecord?.NamaPetugas ?? string.Empty,
                IsSubmit = existingRecord?.IsSubmit ?? false,
            };
        }

        private static void RecalculateTotals(RecordSaveRequest request)
        {
            var timbangan = request.Timbangan ?? new List<RecordTimbanganItem>();

            request.TotalOrganik = timbangan
                .Where(item => string.Equals(item.JenisSampah, "Organik", StringComparison.OrdinalIgnoreCase))
                .Sum(item => item.Berat);

            request.TotalAnorganik = timbangan
                .Where(item => string.Equals(item.JenisSampah, "Anorganik", StringComparison.OrdinalIgnoreCase))
                .Sum(item => item.Berat);

            request.TotalResidu = timbangan
                .Where(item => string.Equals(item.JenisSampah, "Residu", StringComparison.OrdinalIgnoreCase))
                .Sum(item => item.Berat);

            request.TotalTimbangan = timbangan.Sum(item => item.Berat);
        }

        private async Task<RecordSaveResponse> SaveUploadedRecordAsync(
            bool isTps,
            string? nomorSpj,
            string? namaTps,
            string? spjDetailId,
            string? lokasiAngkutId,
            Action<RecordSaveRequest> applyChanges)
        {
            var existingRecord = await FindExistingRecordAsync(nomorSpj, spjDetailId, lokasiAngkutId, namaTps);
            var request = BuildRecordSaveRequest(existingRecord, nomorSpj, namaTps, spjDetailId, lokasiAngkutId);

            applyChanges(request);
            RecalculateTotals(request);

            return isTps
                ? await _detailService.SaveRecordTpsAsync(request)
                : await _detailService.SaveRecordNonTpsAsync(request);
        }

        private void ApplyNoCacheHeaders()
        {
            Response.Headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0";
            Response.Headers["Pragma"] = "no-cache";
            Response.Headers["Expires"] = "0";
        }

        private async Task<RecordSaveRequest?> ResolveRecordSaveRequestAsync()
        {
            Request.EnableBuffering();

            if (Request.Body.CanSeek)
            {
                Request.Body.Position = 0;
            }

            using var reader = new StreamReader(Request.Body, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true);
            var rawBody = await reader.ReadToEndAsync();

            if (Request.Body.CanSeek)
            {
                Request.Body.Position = 0;
            }

            if (string.IsNullOrWhiteSpace(rawBody))
            {
                return null;
            }

            try
            {
                return JsonSerializer.Deserialize<RecordSaveRequest>(rawBody, new JsonSerializerOptions
                {
                    PropertyNameCaseInsensitive = true
                });
            }
            catch (JsonException ex)
            {
                _logger.LogWarning(ex, "Gagal parse body save-record. Body: {RawBody}", rawBody);
                return null;
            }
        }

        [HttpGet("")]
        public IActionResult Index()
        {
            return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Index.cshtml");
        }
        [HttpGet("tanpa-tps")]
        public IActionResult TanpaTps()
        {
            return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/TanpaTps.cshtml");
        }

        [HttpGet("batal")]
        public IActionResult Batal()
        {
            return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/Batal.cshtml");
        }

        [HttpGet("detail-batal")]
        public IActionResult DetailBatal()
        {
            return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/DetailBatal.cshtml");
        }

        [HttpGet("detail-selesai")]
        public IActionResult DetailSelesai()
        {
            return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/DetailSelesai.cshtml");
        }

        [HttpGet("detail-selesai-tanpa-tps")]
        public IActionResult DetailSelesaiTanpaTps()
        {
            return View("~/Views/Admin/Transport/SpjDriverUpst/DetailPenjemputan/DetailSelesaiTanpaTps.cshtml");
        }

        [HttpGet("api/submitted")]
        [IgnoreAntiforgeryToken]
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public async Task<IActionResult> GetSubmittedBySpj([FromQuery] string nomorSpj)
        {
            ApplyNoCacheHeaders();

            if (string.IsNullOrWhiteSpace(nomorSpj))
            {
                return BadRequest(new { success = false, message = "nomorSpj wajib diisi." });
            }

            var items = await _detailService.GetSubmittedByNomorSpjAsync(nomorSpj);
            return Ok(new { success = true, items });
        }

        [HttpGet("api/records")]
        [IgnoreAntiforgeryToken]
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public async Task<IActionResult> GetRecordsBySpj([FromQuery] string nomorSpj)
        {
            ApplyNoCacheHeaders();

            if (string.IsNullOrWhiteSpace(nomorSpj))
            {
                return BadRequest(new { success = false, message = "nomorSpj wajib diisi." });
            }

            var items = await _detailService.GetRecordsByNomorSpjAsync(nomorSpj);
            return Ok(new { success = true, items });
        }

        [HttpGet("api/submitted/detail")]
        [IgnoreAntiforgeryToken]
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public async Task<IActionResult> GetSubmittedDetail(
            [FromQuery] string nomorSpj,
            [FromQuery] string? spjDetailId = null,
            [FromQuery] string? lokasiAngkutId = null,
            [FromQuery] string? namaTps = null)
        {
            ApplyNoCacheHeaders();

            if (string.IsNullOrWhiteSpace(nomorSpj))
            {
                return BadRequest(new { success = false, message = "nomorSpj wajib diisi." });
            }

            var item = await _detailService.GetSubmittedDetailAsync(nomorSpj, spjDetailId, lokasiAngkutId, namaTps);
            return Ok(new { success = true, hasData = item != null, item });
        }

        [HttpGet("api/records/detail")]
        [IgnoreAntiforgeryToken]
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public async Task<IActionResult> GetRecordDetail(
            [FromQuery] string nomorSpj,
            [FromQuery] string? spjDetailId = null,
            [FromQuery] string? lokasiAngkutId = null,
            [FromQuery] string? namaTps = null)
        {
            ApplyNoCacheHeaders();

            if (string.IsNullOrWhiteSpace(nomorSpj))
            {
                return BadRequest(new { success = false, message = "nomorSpj wajib diisi." });
            }

            var item = await _detailService.GetRecordDetailAsync(nomorSpj, spjDetailId, lokasiAngkutId, namaTps);
            return Ok(new { success = true, hasData = item != null, item });
        }

        [HttpPost("")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Submit([FromForm] DetailPenjemputanRequest request)
        {
            var isAjaxRequest = string.Equals(Request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.OrdinalIgnoreCase)
                || Request.Headers.Accept.Any(value => value?.Contains("application/json", StringComparison.OrdinalIgnoreCase) == true);

            try
            {
                var result = await _detailService.SubmitPenjemputanAsync(request);

                if (isAjaxRequest)
                {
                    return result.Success
                        ? Ok(result)
                        : BadRequest(result);
                }

                if (result.Success)
                {
                    TempData["Success"] = result.Message;
                }
                else
                {
                    TempData["Error"] = result.Message;
                }
                
                return RedirectToAction(nameof(Index));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error submitting penjemputan data");

                if (isAjaxRequest)
                {
                    return StatusCode(500, new DetailPenjemputanResponse
                    {
                        Success = false,
                        Message = "Terjadi kesalahan saat menyimpan data."
                    });
                }

                TempData["Error"] = "Terjadi kesalahan saat menyimpan data.";
                return RedirectToAction(nameof(Index));
            }
        }

        [HttpPost("save-record-non-tps")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> SaveRecordNonTps()
        {
            var request = await ResolveRecordSaveRequestAsync();

            if (request == null)
                return BadRequest(new RecordSaveResponse { Success = false, Message = "Request tidak valid." });

            var result = await _detailService.SaveRecordNonTpsAsync(request);
            return result.Success ? Ok(result) : StatusCode(500, result);
        }

        [HttpPost("save-record")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> SaveRecord()
        {
            var request = await ResolveRecordSaveRequestAsync();

            if (request == null)
                return BadRequest(new RecordSaveResponse { Success = false, Message = "Request tidak valid." });

            var result = await _detailService.SaveRecordTpsAsync(request);
            return result.Success ? Ok(result) : StatusCode(500, result);
        }


        [HttpPost("upload-foto-kedatangan-non-tps")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> UploadFotoKedatanganNonTps(
            [FromForm] List<IFormFile>? FotoKedatangan,
            [FromForm] string? NomorSpj,
            [FromForm] string? NamaTps,
            [FromForm] string? SpjDetailId,
            [FromForm] string? LokasiAngkutId,
            [FromForm] string? WaktuKedatangan,
            [FromForm] string? Latitude,
            [FromForm] string? Longitude,
            [FromForm] string? AlamatJalan)
        {
            if (FotoKedatangan == null || FotoKedatangan.Count == 0)
                return BadRequest(new { success = false, message = "Tidak ada foto." });

            var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
            var uploadDir = GetUploadDirectory(dateFolder, NomorSpj, NamaTps);

            var fileNames = new List<string>();
            foreach (var file in FotoKedatangan)
            {
                if (file.Length == 0) continue;
                var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
                var name = $"kedatangan_{Guid.NewGuid()}{ext}";
                var path = Path.Combine(uploadDir, name);
                await using var stream = new FileStream(path, FileMode.Create);
                await file.CopyToAsync(stream);
                fileNames.Add(name);
            }
            var fileUrls = fileNames.Select(n => BuildUploadUrl(dateFolder, n, NomorSpj, NamaTps)).ToList();

            var saveResult = await SaveUploadedRecordAsync(
                isTps: false,
                nomorSpj: NomorSpj,
                namaTps: NamaTps,
                spjDetailId: SpjDetailId,
                lokasiAngkutId: LokasiAngkutId,
                applyChanges: request =>
                {
                    request.WaktuKedatangan = string.IsNullOrWhiteSpace(WaktuKedatangan) ? request.WaktuKedatangan : WaktuKedatangan;
                    request.Latitude = string.IsNullOrWhiteSpace(Latitude) ? request.Latitude : Latitude;
                    request.Longitude = string.IsNullOrWhiteSpace(Longitude) ? request.Longitude : Longitude;
                    request.AlamatJalan = string.IsNullOrWhiteSpace(AlamatJalan) ? request.AlamatJalan : AlamatJalan;
                    request.FotoKedatanganFileNames = fileUrls;
                    request.FotoKedatanganUploaded = true;
                });

            if (!saveResult.Success)
            {
                return StatusCode(500, new { success = false, message = saveResult.Message });
            }

            return Ok(new { success = true, fileNames, fileUrls, message = $"{fileNames.Count} foto kedatangan berhasil diupload." });
        }

        [HttpPost("upload-foto-timbangan-non-tps")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> UploadFotoTimbanganNonTps(
            [FromForm] IFormFile? FotoTimbangan,
            [FromForm] string? NomorSpj,
            [FromForm] string? NamaTps,
            [FromForm] string? SpjDetailId,
            [FromForm] string? LokasiAngkutId,
            [FromForm] int ItemIndex,
            [FromForm] string? JenisSampah,
            [FromForm] decimal Berat)
        {
            if (FotoTimbangan == null || FotoTimbangan.Length == 0)
                return BadRequest(new { success = false, message = "Tidak ada foto." });

            var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
            var uploadDir = GetUploadDirectory(dateFolder, NomorSpj, NamaTps);

            var ext = Path.GetExtension(FotoTimbangan.FileName).ToLowerInvariant();
            var jenisSafe = (JenisSampah ?? "residu").ToLowerInvariant();
            var beratStr = Berat.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture).Replace('.', '_');
            var name = $"timbangan{ItemIndex + 1}-{jenisSafe}-{beratStr}{ext}";
            var filePath = Path.Combine(uploadDir, name);
            await using var stream = new FileStream(filePath, FileMode.Create);
            await FotoTimbangan.CopyToAsync(stream);
            var fileUrl = BuildUploadUrl(dateFolder, name, NomorSpj, NamaTps);

            var saveResult = await SaveUploadedRecordAsync(
                isTps: false,
                nomorSpj: NomorSpj,
                namaTps: NamaTps,
                spjDetailId: SpjDetailId,
                lokasiAngkutId: LokasiAngkutId,
                applyChanges: request =>
                {
                    while (request.Timbangan.Count <= ItemIndex)
                    {
                        request.Timbangan.Add(new RecordTimbanganItem());
                    }

                    request.Timbangan[ItemIndex] = new RecordTimbanganItem
                    {
                        FotoFileName = fileUrl,
                        JenisSampah = JenisSampah ?? "Residu",
                        Berat = Berat,
                        Uploaded = true
                    };
                });

            if (!saveResult.Success)
            {
                return StatusCode(500, new { success = false, message = saveResult.Message });
            }

            return Ok(new { success = true, fileName = name, fileUrl, message = $"Foto timbangan #{ItemIndex + 1} berhasil diupload." });
        }

        [HttpPost("upload-foto-petugas-non-tps")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> UploadFotoPetugasNonTps(
            [FromForm] List<IFormFile>? FotoPetugas,
            [FromForm] string? NomorSpj,
            [FromForm] string? NamaTps,
            [FromForm] string? SpjDetailId,
            [FromForm] string? LokasiAngkutId,
            [FromForm] string? NamaPetugas)
        {
            if (FotoPetugas == null || FotoPetugas.Count == 0)
                return BadRequest(new { success = false, message = "Tidak ada foto." });

            var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
            var uploadDir = GetUploadDirectory(dateFolder, NomorSpj, NamaTps);

            var fileNames = new List<string>();
            foreach (var file in FotoPetugas)
            {
                if (file.Length == 0) continue;
                var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
                var name = $"petugas_{Guid.NewGuid()}{ext}";
                var path = Path.Combine(uploadDir, name);
                await using var stream = new FileStream(path, FileMode.Create);
                await file.CopyToAsync(stream);
                fileNames.Add(name);
            }
            var fileUrls = fileNames.Select(n => BuildUploadUrl(dateFolder, n, NomorSpj, NamaTps)).ToList();

            var saveResult = await SaveUploadedRecordAsync(
                isTps: false,
                nomorSpj: NomorSpj,
                namaTps: NamaTps,
                spjDetailId: SpjDetailId,
                lokasiAngkutId: LokasiAngkutId,
                applyChanges: request =>
                {
                    request.FotoPetugasFileNames = fileUrls;
                    request.FotoPetugasUploaded = true;
                    request.NamaPetugas = string.IsNullOrWhiteSpace(NamaPetugas) ? request.NamaPetugas : NamaPetugas;
                });

            if (!saveResult.Success)
            {
                return StatusCode(500, new { success = false, message = saveResult.Message });
            }

            return Ok(new { success = true, fileNames, fileUrls, message = $"{fileNames.Count} foto petugas berhasil diupload." });
        }

        [HttpPost("upload-foto-kedatangan")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> UploadFotoKedatangan(
            [FromForm] List<IFormFile>? FotoKedatangan,
            [FromForm] string? NomorSpj,
            [FromForm] string? NamaTps,
            [FromForm] string? SpjDetailId,
            [FromForm] string? LokasiAngkutId,
            [FromForm] string? WaktuKedatangan,
            [FromForm] string? Latitude,
            [FromForm] string? Longitude,
            [FromForm] string? AlamatJalan)
        {
            if (FotoKedatangan == null || FotoKedatangan.Count == 0)
                return BadRequest(new { success = false, message = "Tidak ada foto." });

            var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
            var uploadDir = GetUploadDirectory(dateFolder, NomorSpj, NamaTps);

            var fileNames = new List<string>();
            foreach (var file in FotoKedatangan)
            {
                if (file.Length == 0) continue;
                var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
                var name = $"kedatangan_{Guid.NewGuid()}{ext}";
                var path = Path.Combine(uploadDir, name);
                await using var stream = new FileStream(path, FileMode.Create);
                await file.CopyToAsync(stream);
                fileNames.Add(name);
            }
            var fileUrls = fileNames.Select(n => BuildUploadUrl(dateFolder, n, NomorSpj, NamaTps)).ToList();

            var saveResult = await SaveUploadedRecordAsync(
                isTps: true,
                nomorSpj: NomorSpj,
                namaTps: NamaTps,
                spjDetailId: SpjDetailId,
                lokasiAngkutId: LokasiAngkutId,
                applyChanges: request =>
                {
                    request.WaktuKedatangan = string.IsNullOrWhiteSpace(WaktuKedatangan) ? request.WaktuKedatangan : WaktuKedatangan;
                    request.Latitude = string.IsNullOrWhiteSpace(Latitude) ? request.Latitude : Latitude;
                    request.Longitude = string.IsNullOrWhiteSpace(Longitude) ? request.Longitude : Longitude;
                    request.AlamatJalan = string.IsNullOrWhiteSpace(AlamatJalan) ? request.AlamatJalan : AlamatJalan;
                    request.FotoKedatanganFileNames = fileUrls;
                    request.FotoKedatanganUploaded = true;
                });

            if (!saveResult.Success)
            {
                return StatusCode(500, new { success = false, message = saveResult.Message });
            }

            return Ok(new { success = true, fileNames, fileUrls, message = $"{fileNames.Count} foto kedatangan berhasil diupload." });
        }

        [HttpPost("upload-foto-timbangan")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> UploadFotoTimbangan(
            [FromForm] IFormFile? FotoTimbangan,
            [FromForm] string? NomorSpj,
            [FromForm] string? NamaTps,
            [FromForm] string? SpjDetailId,
            [FromForm] string? LokasiAngkutId,
            [FromForm] int ItemIndex,
            [FromForm] string? JenisSampah,
            [FromForm] decimal Berat)
        {
            if (FotoTimbangan == null || FotoTimbangan.Length == 0)
                return BadRequest(new { success = false, message = "Tidak ada foto." });

            var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
            var uploadDir = GetUploadDirectory(dateFolder, NomorSpj, NamaTps);

            var ext = Path.GetExtension(FotoTimbangan.FileName).ToLowerInvariant();
            var jenisSafe = (JenisSampah ?? "residu").ToLowerInvariant();
            var beratStr = Berat.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture).Replace('.', '_');
            var name = $"timbangan{ItemIndex + 1}-{jenisSafe}-{beratStr}{ext}";
            var filePath = Path.Combine(uploadDir, name);
            await using var stream = new FileStream(filePath, FileMode.Create);
            await FotoTimbangan.CopyToAsync(stream);
            var fileUrl = BuildUploadUrl(dateFolder, name, NomorSpj, NamaTps);

            var saveResult = await SaveUploadedRecordAsync(
                isTps: true,
                nomorSpj: NomorSpj,
                namaTps: NamaTps,
                spjDetailId: SpjDetailId,
                lokasiAngkutId: LokasiAngkutId,
                applyChanges: request =>
                {
                    while (request.Timbangan.Count <= ItemIndex)
                    {
                        request.Timbangan.Add(new RecordTimbanganItem());
                    }

                    request.Timbangan[ItemIndex] = new RecordTimbanganItem
                    {
                        FotoFileName = fileUrl,
                        JenisSampah = JenisSampah ?? "Residu",
                        Berat = Berat,
                        Uploaded = true
                    };
                });

            if (!saveResult.Success)
            {
                return StatusCode(500, new { success = false, message = saveResult.Message });
            }

            return Ok(new { success = true, fileName = name, fileUrl, message = $"Foto timbangan #{ItemIndex + 1} berhasil diupload." });
        }

        [HttpPost("upload-foto-petugas")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> UploadFotoPetugas(
            [FromForm] List<IFormFile>? FotoPetugas,
            [FromForm] string? NomorSpj,
            [FromForm] string? NamaTps,
            [FromForm] string? SpjDetailId,
            [FromForm] string? LokasiAngkutId,
            [FromForm] string? NamaPetugas)
        {
            if (FotoPetugas == null || FotoPetugas.Count == 0)
                return BadRequest(new { success = false, message = "Tidak ada foto." });

            var dateFolder = DateTime.Now.ToString("yyyy-MM-dd");
            var uploadDir = GetUploadDirectory(dateFolder, NomorSpj, NamaTps);

            var fileNames = new List<string>();
            foreach (var file in FotoPetugas)
            {
                if (file.Length == 0) continue;
                var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
                var name = $"petugas_{Guid.NewGuid()}{ext}";
                var path = Path.Combine(uploadDir, name);
                await using var stream = new FileStream(path, FileMode.Create);
                await file.CopyToAsync(stream);
                fileNames.Add(name);
            }
            var fileUrls = fileNames.Select(n => BuildUploadUrl(dateFolder, n, NomorSpj, NamaTps)).ToList();

            var saveResult = await SaveUploadedRecordAsync(
                isTps: true,
                nomorSpj: NomorSpj,
                namaTps: NamaTps,
                spjDetailId: SpjDetailId,
                lokasiAngkutId: LokasiAngkutId,
                applyChanges: request =>
                {
                    request.FotoPetugasFileNames = fileUrls;
                    request.FotoPetugasUploaded = true;
                    request.NamaPetugas = string.IsNullOrWhiteSpace(NamaPetugas) ? request.NamaPetugas : NamaPetugas;
                });

            if (!saveResult.Success)
            {
                return StatusCode(500, new { success = false, message = saveResult.Message });
            }

            return Ok(new { success = true, fileNames, fileUrls, message = $"{fileNames.Count} foto petugas berhasil diupload." });
        }


        [HttpPost("ocr-timbangan")]
        [IgnoreAntiforgeryToken]
        public async Task<IActionResult> OcrTimbangan(IFormFile? Foto)
        {
            if (Foto == null || Foto.Length == 0)
            {
                return BadRequest(new { success = false, message = "Foto tidak ditemukan." });
            }

            if (Foto.Length > 5 * 1024 * 1024)
            {
                return BadRequest(new { success = false, message = "Ukuran foto terlalu besar. Maksimal 5MB." });
            }

            var apiKey = _configuration["OpenRouter:OCRkey"];
            if (string.IsNullOrWhiteSpace(apiKey))
            {
                return StatusCode(500, new { success = false, message = "OpenRouter API key belum diset." });
            }

            byte[] fileBytes;
            await using (var ms = new MemoryStream())
            {
                await Foto.CopyToAsync(ms);
                fileBytes = ms.ToArray();
            }

            var mimeType = string.IsNullOrWhiteSpace(Foto.ContentType) ? "image/jpeg" : Foto.ContentType;
            var base64 = Convert.ToBase64String(fileBytes);
            var dataUrl = $"data:{mimeType};base64,{base64}";

            var payload = new
            {
                // model = "nvidia/nemotron-nano-12b-v2-vl:free",
                model = "google/gemini-2.5-flash-image",
                // model = "google/gemini-2.5-flash-lite",
                // model = "google/gemini-2.5-flash-lite-preview-09-2025",
                temperature = 0,
                messages = new object[]
                {
                    new
                    {
                        role = "user",
                        content = new object[]
                        {
                            new
                            {
                                type = "text",
                                text = @"
                                Baca angka berat timbangan digital pada foto.

                                Rules:
                                - Abaikan tulisan seperti ZERO, TARE, STABLE, AC, PACK, PCS, KG, ADD, HOLD.
                                - Jawab hanya angka dengan format 2 digit desimal pakai titik (contoh: 54.45).
                                - Jika tidak terbaca jawab: UNREADABLE
                                - Fokus pada angka layar LED merah yang menyala.
                                - Abaikan refleksi atau pantulan cahaya yang mungkin muncul di layar.
                                - Abaikan timestamp seperti tanggal, jam, atau informasi lain yang biasanya muncul di layar timbangan.
                                "
                            },

                            new
                            {
                                type = "image_url",
                                image_url = new { url = dataUrl }
                            }
                        }
                    }
                }
            };

            var json = JsonSerializer.Serialize(payload);

            var request = new HttpRequestMessage(HttpMethod.Post, "https://openrouter.ai/api/v1/chat/completions");
            request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {apiKey}");
            request.Headers.TryAddWithoutValidation("Accept", "application/json");
            request.Headers.TryAddWithoutValidation("HTTP-Referer", "https://pesapakawan.dinaslhdki.id");
            request.Headers.TryAddWithoutValidation("X-Title", "eSPJ OCR Timbangan");

            request.Content = new StringContent(json, Encoding.UTF8, "application/json");

            var client = _httpClientFactory.CreateClient();
            using var response = await client.SendAsync(request);
            var responseText = await response.Content.ReadAsStringAsync();

            if (!response.IsSuccessStatusCode)
            {
                return StatusCode((int)response.StatusCode, new
                {
                    success = false,
                    message = "OpenRouter request gagal.",
                    detail = responseText
                });
            }

            using var doc = JsonDocument.Parse(responseText);

            var content = doc.RootElement
                .GetProperty("choices")[0]
                .GetProperty("message")
                .GetProperty("content")
                .GetString() ?? "";

            content = content.Trim();

            if (content.Contains("UNREADABLE", StringComparison.OrdinalIgnoreCase))
            {
                return Ok(new
                {
                    success = false,
                    message = "Angka tidak terbaca.",
                    raw = content
                });
            }

            // cari format angka 2 desimal
            var match = Regex.Match(content, @"-?\d{1,5}([.,]\d{2})");

            if (!match.Success)
            {
                return Ok(new
                {
                    success = false,
                    message = "AI tidak menemukan angka valid.",
                    raw = content
                });
            }

            var normalized = match.Value.Replace(',', '.');

            if (!decimal.TryParse(normalized, NumberStyles.Any, CultureInfo.InvariantCulture, out var weight))
            {
                return Ok(new
                {
                    success = false,
                    message = "Format angka AI tidak valid.",
                    raw = content
                });
            }

            return Ok(new
            {
                success = true,
                weight = weight.ToString("0.00", CultureInfo.InvariantCulture),
                raw = content
            });
        }

    }
}
