using Media.JoshHeaps.Net.Services;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace Media.JoshHeaps.Net.Api;

[ApiController]
[Route("api/medical-docs")]
public class MedicalDocsApi(DbExecutor dbExecutor, MedicalDocsService medicalDocsService, MedicalAiService medicalAiService) : ControllerBase
{
    // --- People ---

    [HttpGet("people")]
    public async Task<IActionResult> GetPeople()
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        var people = await medicalDocsService.GetPeopleAsync(userId.Value);
        return Ok(people);
    }

    [HttpPost("people")]
    public async Task<IActionResult> CreatePerson([FromBody] CreatePersonRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        if (string.IsNullOrWhiteSpace(request.Name))
            return BadRequest(new { error = "Name is required" });

        var person = await medicalDocsService.CreatePersonAsync(userId.Value, request.Name.Trim(), request.DateOfBirth, request.Notes);
        if (person == null)
            return StatusCode(500, new { error = "Failed to create person" });

        return Ok(person);
    }

    // --- People Access ---

    [HttpGet("people/{personId}/access")]
    public async Task<IActionResult> GetPersonAccess(long personId)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasPersonAccess(userId.Value, personId)) return Forbid();

        var users = await medicalDocsService.GetPeopleAccessAsync(personId);
        return Ok(users);
    }

    [HttpPost("people/{personId}/access")]
    public async Task<IActionResult> GrantPersonAccess(long personId, [FromBody] GrantAccessRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasPersonAccess(userId.Value, personId)) return Forbid();

        if (string.IsNullOrWhiteSpace(request.Username))
            return BadRequest(new { error = "Username is required" });

        var targetUser = await dbExecutor.ExecuteReaderAsync(
            "SELECT id FROM app.users WHERE LOWER(username) = LOWER(@username)",
            reader => reader.GetInt64(0),
            new { username = request.Username.Trim() });

        if (targetUser == 0)
            return NotFound(new { error = "User not found" });

        var success = await medicalDocsService.GrantAccessAsync(personId, targetUser);
        if (!success)
            return StatusCode(500, new { error = "Failed to grant access" });

        return Ok(new { success = true });
    }

    [HttpDelete("people/{personId}/access/{targetUserId}")]
    public async Task<IActionResult> RevokePersonAccess(long personId, long targetUserId)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasPersonAccess(userId.Value, personId)) return Forbid();

        var success = await medicalDocsService.RevokeAccessAsync(personId, targetUserId);
        if (!success)
            return BadRequest(new { error = "Cannot revoke access — at least one user must have access" });

        return Ok(new { success = true });
    }

    // --- Documents ---

    [HttpGet("documents")]
    public async Task<IActionResult> GetDocuments([FromQuery] long? personId, [FromQuery] int offset = 0, [FromQuery] int limit = 50)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (personId.HasValue && !await HasPersonAccess(userId.Value, personId.Value)) return Forbid();

        if (limit < 1 || limit > 100) limit = 50;
        if (offset < 0) offset = 0;

        var documents = await medicalDocsService.GetDocumentsAsync(personId, offset, limit);
        return Ok(documents);
    }

    [HttpGet("documents/search")]
    public async Task<IActionResult> SearchDocuments(
        [FromQuery] long? personId = null,
        [FromQuery] string? search = null,
        [FromQuery] string? classification = null,
        [FromQuery] string? documentType = null,
        [FromQuery] long? doctorId = null,
        [FromQuery] long? tagId = null,
        [FromQuery] long? conditionId = null,
        [FromQuery] DateTime? fromDate = null,
        [FromQuery] DateTime? toDate = null,
        [FromQuery] bool? aiProcessed = null,
        [FromQuery] int offset = 0,
        [FromQuery] int limit = 50)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (personId.HasValue && !await HasPersonAccess(userId.Value, personId.Value)) return Forbid();

        if (limit < 1 || limit > 100) limit = 50;
        if (offset < 0) offset = 0;

        var documents = await medicalDocsService.SearchDocumentsAsync(personId, search, classification, documentType, doctorId, tagId, conditionId, fromDate, toDate, aiProcessed, accessUserId: personId.HasValue ? null : userId, offset: offset, limit: limit);
        return Ok(documents);
    }

    [HttpGet("tags")]
    public async Task<IActionResult> GetPersonTags([FromQuery] long? personId = null)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (personId.HasValue && !await HasPersonAccess(userId.Value, personId.Value)) return Forbid();

        var tags = await medicalDocsService.GetPersonTagsAsync(personId, accessUserId: personId.HasValue ? null : userId);
        return Ok(tags);
    }

    [HttpPost("documents/upload")]
    [RequestSizeLimit(52_428_800)] // 50MB
    public async Task<IActionResult> UploadDocument([FromForm] long personId, [FromForm] string? title, [FromForm] string? description, [FromForm] DateTime? documentDate, [FromForm] string? classification, IFormFile file)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasPersonAccess(userId.Value, personId)) return Forbid();

        if (file == null || file.Length == 0)
            return BadRequest(new { error = "No file provided" });

        var doc = await medicalDocsService.SaveDocumentAsync(personId, file, title, description, documentDate, classification);
        if (doc == null)
            return StatusCode(500, new { error = "Failed to save document" });

        medicalAiService.EnqueueProcessing(doc.Id);

        return Ok(doc);
    }

    [HttpPost("documents/note")]
    public async Task<IActionResult> CreateNote([FromBody] CreateNoteRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        if (request.PersonId <= 0)
            return BadRequest(new { error = "Person is required" });
        if (!await HasPersonAccess(userId.Value, request.PersonId)) return Forbid();
        if (string.IsNullOrWhiteSpace(request.Title))
            return BadRequest(new { error = "Title is required" });

        var doc = await medicalDocsService.SaveNoteAsync(request.PersonId, request.Title.Trim(), request.Description ?? "", request.DocumentDate, request.Classification);
        if (doc == null)
            return StatusCode(500, new { error = "Failed to create note" });

        medicalAiService.EnqueueProcessing(doc.Id);

        return Ok(doc);
    }

    [HttpGet("documents/{id}")]
    public async Task<IActionResult> GetDocument(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "document", id)) return Forbid();

        var doc = await medicalDocsService.GetDocumentByIdAsync(id);
        if (doc == null) return NotFound(new { error = "Document not found" });

        return Ok(doc);
    }

    [HttpGet("documents/{id}/download")]
    public async Task<IActionResult> DownloadDocument(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "document", id)) return Forbid();

        var doc = await medicalDocsService.GetDocumentByIdAsync(id);
        if (doc == null) return NotFound(new { error = "Document not found" });

        if (doc.DocumentType != "file")
            return BadRequest(new { error = "Cannot download a note" });

        var data = await medicalDocsService.GetDecryptedDocumentDataAsync(id);
        if (data == null) return NotFound(new { error = "File not found" });

        return File(data, doc.MimeType ?? "application/octet-stream", doc.FileName);
    }

    [HttpPut("documents/{id}")]
    public async Task<IActionResult> UpdateDocument(long id, [FromBody] UpdateDocumentRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "document", id)) return Forbid();

        var success = await medicalDocsService.UpdateDocumentAsync(id, request.Title, request.Description, request.DocumentDate, request.Classification, request.DoctorId);
        if (!success) return NotFound(new { error = "Document not found" });

        return Ok(new { success = true });
    }

    [HttpDelete("documents/{id}")]
    public async Task<IActionResult> DeleteDocument(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "document", id)) return Forbid();

        var success = await medicalDocsService.DeleteDocumentAsync(id);
        if (!success) return NotFound(new { error = "Document not found" });

        return Ok(new { success = true });
    }

    // --- AI Processing ---

    [HttpPost("documents/{id}/process")]
    public async Task<IActionResult> ProcessDocument(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "document", id)) return Forbid();

        var doc = await medicalDocsService.GetDocumentByIdAsync(id);
        if (doc == null) return NotFound(new { error = "Document not found" });

        medicalAiService.EnqueueProcessing(id);

        return Ok(new { success = true, message = "AI processing started" });
    }

    [HttpPost("documents/process-all")]
    public async Task<IActionResult> ProcessAllDocuments()
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        var unprocessedIds = await medicalDocsService.GetUnprocessedDocumentIdsAsync();

        foreach (var docId in unprocessedIds)
        {
            medicalAiService.EnqueueProcessing(docId);
        }

        return Ok(new { success = true, queued = unprocessedIds.Count });
    }

    [HttpPost("documents/process-batch")]
    public async Task<IActionResult> ProcessBatch([FromBody] ProcessBatchRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        if (request.DocumentIds == null || request.DocumentIds.Count == 0)
            return BadRequest(new { error = "No document IDs provided" });

        var queued = 0;
        foreach (var docId in request.DocumentIds)
        {
            var doc = await medicalDocsService.GetDocumentByIdAsync(docId);
            if (doc != null)
            {
                medicalAiService.EnqueueProcessing(docId);
                queued++;
            }
        }

        return Ok(new { queued });
    }

    [HttpGet("documents/{id}/tags")]
    public async Task<IActionResult> GetDocumentTags(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "document", id)) return Forbid();

        var tags = await medicalDocsService.GetDocumentTagsAsync(id);
        return Ok(tags);
    }

    // --- Doctors (shared, no per-person access check) ---

    [HttpGet("doctors")]
    public async Task<IActionResult> GetDoctors([FromQuery] long? personId = null)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (personId.HasValue && !await HasPersonAccess(userId.Value, personId.Value)) return Forbid();

        var doctors = await medicalDocsService.GetDoctorsAsync(personId, accessUserId: personId.HasValue ? null : userId);
        return Ok(doctors);
    }

    [HttpPost("doctors")]
    public async Task<IActionResult> CreateDoctor([FromBody] CreateDoctorRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasPersonAccess(userId.Value, request.PersonId)) return Forbid();

        if (string.IsNullOrWhiteSpace(request.Name))
            return BadRequest(new { error = "Name is required" });

        var doctor = await medicalDocsService.CreateDoctorAsync(request.PersonId, request.Name.Trim(), request.Specialty, request.Phone, request.Address, request.Notes);
        if (doctor == null)
            return StatusCode(500, new { error = "Failed to create doctor" });

        return Ok(doctor);
    }

    [HttpPut("doctors/{id}")]
    public async Task<IActionResult> UpdateDoctor(long id, [FromBody] CreateDoctorRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "doctor", id)) return Forbid();

        if (string.IsNullOrWhiteSpace(request.Name))
            return BadRequest(new { error = "Name is required" });

        var success = await medicalDocsService.UpdateDoctorAsync(id, request.Name.Trim(), request.Specialty, request.Phone, request.Address, request.Notes);
        if (!success) return NotFound(new { error = "Doctor not found" });

        return Ok(new { success = true });
    }

    [HttpDelete("doctors/{id}")]
    public async Task<IActionResult> DeleteDoctor(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "doctor", id)) return Forbid();

        var success = await medicalDocsService.DeleteDoctorAsync(id);
        if (!success) return NotFound(new { error = "Doctor not found" });

        return Ok(new { success = true });
    }

    // --- Conditions ---

    [HttpGet("conditions")]
    public async Task<IActionResult> GetConditions([FromQuery] long? personId = null)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (personId.HasValue && !await HasPersonAccess(userId.Value, personId.Value)) return Forbid();

        var conditions = await medicalDocsService.GetConditionsAsync(personId, accessUserId: personId.HasValue ? null : userId);
        return Ok(conditions);
    }

    [HttpPost("conditions")]
    public async Task<IActionResult> CreateCondition([FromBody] CreateConditionRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        if (request.PersonId <= 0)
            return BadRequest(new { error = "Person is required" });
        if (!await HasPersonAccess(userId.Value, request.PersonId)) return Forbid();
        if (string.IsNullOrWhiteSpace(request.Name))
            return BadRequest(new { error = "Name is required" });

        var condition = await medicalDocsService.CreateConditionAsync(request.PersonId, request.Name.Trim(), request.DiagnosedDate, request.Notes);
        if (condition == null)
            return StatusCode(500, new { error = "Failed to create condition" });

        return Ok(condition);
    }

    [HttpPut("conditions/{id}")]
    public async Task<IActionResult> UpdateCondition(long id, [FromBody] UpdateConditionRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "condition", id)) return Forbid();

        if (string.IsNullOrWhiteSpace(request.Name))
            return BadRequest(new { error = "Name is required" });

        var success = await medicalDocsService.UpdateConditionAsync(id, request.Name.Trim(), request.DiagnosedDate, request.Notes, request.IsActive);
        if (!success) return NotFound(new { error = "Condition not found" });

        return Ok(new { success = true });
    }

    [HttpDelete("conditions/{id}")]
    public async Task<IActionResult> DeleteCondition(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "condition", id)) return Forbid();

        var success = await medicalDocsService.DeleteConditionAsync(id);
        if (!success) return NotFound(new { error = "Condition not found" });

        return Ok(new { success = true });
    }

    // --- Prescriptions ---

    [HttpGet("prescriptions")]
    public async Task<IActionResult> GetPrescriptions([FromQuery] long? personId = null)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (personId.HasValue && !await HasPersonAccess(userId.Value, personId.Value)) return Forbid();

        var prescriptions = await medicalDocsService.GetPrescriptionsAsync(personId, accessUserId: personId.HasValue ? null : userId);
        return Ok(prescriptions);
    }

    [HttpPost("prescriptions")]
    public async Task<IActionResult> CreatePrescription([FromBody] CreatePrescriptionRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        if (request.PersonId <= 0)
            return BadRequest(new { error = "Person is required" });
        if (!await HasPersonAccess(userId.Value, request.PersonId)) return Forbid();
        if (string.IsNullOrWhiteSpace(request.MedicationName))
            return BadRequest(new { error = "Medication name is required" });

        var prescription = await medicalDocsService.CreatePrescriptionAsync(request.PersonId, request.MedicationName.Trim(), request.Dosage, request.Frequency, request.DoctorId, request.StartDate, request.Notes, request.RxNumber?.Trim());
        if (prescription == null)
            return StatusCode(500, new { error = "Failed to create prescription" });

        return Ok(prescription);
    }

    [HttpPut("prescriptions/{id}")]
    public async Task<IActionResult> UpdatePrescription(long id, [FromBody] UpdatePrescriptionRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "prescription", id)) return Forbid();

        if (string.IsNullOrWhiteSpace(request.MedicationName))
            return BadRequest(new { error = "Medication name is required" });

        var success = await medicalDocsService.UpdatePrescriptionAsync(id, request.MedicationName.Trim(), request.Dosage, request.Frequency, request.DoctorId, request.StartDate, request.EndDate, request.Notes, request.IsActive, request.RxNumber?.Trim());
        if (!success) return NotFound(new { error = "Prescription not found" });

        return Ok(new { success = true });
    }

    [HttpDelete("prescriptions/{id}")]
    public async Task<IActionResult> DeletePrescription(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "prescription", id)) return Forbid();

        var success = await medicalDocsService.DeletePrescriptionAsync(id);
        if (!success) return NotFound(new { error = "Prescription not found" });

        return Ok(new { success = true });
    }

    // --- Pickups ---

    [HttpGet("prescriptions/{id}/pickups")]
    public async Task<IActionResult> GetPickups(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "prescription", id)) return Forbid();

        var pickups = await medicalDocsService.GetPickupsAsync(id);
        return Ok(pickups);
    }

    [HttpPost("prescriptions/{id}/pickups")]
    public async Task<IActionResult> CreatePickup(long id, [FromBody] CreatePickupRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "prescription", id)) return Forbid();

        var pickup = await medicalDocsService.CreatePickupAsync(id, request.PickupDate, request.Quantity, request.Pharmacy, request.Cost, request.Notes);
        if (pickup == null)
            return StatusCode(500, new { error = "Failed to create pickup" });

        return Ok(pickup);
    }

    [HttpDelete("pickups/{id}")]
    public async Task<IActionResult> DeletePickup(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "pickup", id)) return Forbid();

        var success = await medicalDocsService.DeletePickupAsync(id);
        if (!success) return NotFound(new { error = "Pickup not found" });

        return Ok(new { success = true });
    }

    // --- Billing Providers ---

    [HttpGet("providers")]
    public async Task<IActionResult> GetProviders([FromQuery] long? personId = null)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (personId.HasValue && !await HasPersonAccess(userId.Value, personId.Value)) return Forbid();

        var providers = await medicalDocsService.GetProvidersAsync(personId, accessUserId: personId.HasValue ? null : userId);
        return Ok(providers);
    }

    [HttpPost("providers")]
    public async Task<IActionResult> CreateProvider([FromBody] CreateProviderRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        if (request.PersonId <= 0)
            return BadRequest(new { error = "Person is required" });
        if (!await HasPersonAccess(userId.Value, request.PersonId)) return Forbid();
        if (string.IsNullOrWhiteSpace(request.Name))
            return BadRequest(new { error = "Name is required" });

        var provider = await medicalDocsService.CreateProviderAsync(request.PersonId, request.Name.Trim(), request.Notes);
        if (provider == null)
            return StatusCode(500, new { error = "Failed to create provider" });

        return Ok(provider);
    }

    [HttpPut("providers/{id}")]
    public async Task<IActionResult> UpdateProvider(long id, [FromBody] UpdateProviderRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "provider", id)) return Forbid();

        if (string.IsNullOrWhiteSpace(request.Name))
            return BadRequest(new { error = "Name is required" });

        var success = await medicalDocsService.UpdateProviderAsync(id, request.Name.Trim(), request.Notes);
        if (!success) return NotFound(new { error = "Provider not found" });

        return Ok(new { success = true });
    }

    [HttpDelete("providers/{id}")]
    public async Task<IActionResult> DeleteProvider(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "provider", id)) return Forbid();

        var success = await medicalDocsService.DeleteProviderAsync(id);
        if (!success) return NotFound(new { error = "Provider not found" });

        return Ok(new { success = true });
    }

    // --- Provider Payments ---

    [HttpGet("providers/{id}/payments")]
    public async Task<IActionResult> GetProviderPayments(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "provider", id)) return Forbid();

        var payments = await medicalDocsService.GetProviderPaymentsAsync(id);
        return Ok(payments);
    }

    [HttpPost("providers/{id}/payments")]
    public async Task<IActionResult> CreateProviderPayment(long id, [FromBody] CreateProviderPaymentRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "provider", id)) return Forbid();

        if (request.Amount <= 0)
            return BadRequest(new { error = "Amount must be greater than 0" });

        var payment = await medicalDocsService.CreateProviderPaymentAsync(id, request.Amount, request.PaymentDate, request.Description);
        if (payment == null)
            return StatusCode(500, new { error = "Failed to create payment" });

        return Ok(payment);
    }

    [HttpDelete("provider-payments/{id}")]
    public async Task<IActionResult> DeleteProviderPayment(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "provider-payment", id)) return Forbid();

        var success = await medicalDocsService.DeleteProviderPaymentAsync(id);
        if (!success) return NotFound(new { error = "Payment not found" });

        return Ok(new { success = true });
    }

    // --- Bills ---

    [HttpGet("bills")]
    public async Task<IActionResult> GetBills([FromQuery] long? personId = null, [FromQuery] long? providerId = null)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (personId.HasValue && !await HasPersonAccess(userId.Value, personId.Value)) return Forbid();

        var bills = await medicalDocsService.GetBillsAsync(personId, providerId, accessUserId: personId.HasValue ? null : userId);
        return Ok(bills);
    }

    [HttpPost("bills")]
    public async Task<IActionResult> CreateBill([FromBody] CreateBillRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        if (request.PersonId <= 0)
            return BadRequest(new { error = "Person is required" });
        if (!await HasPersonAccess(userId.Value, request.PersonId)) return Forbid();
        if (request.TotalAmount <= 0)
            return BadRequest(new { error = "Amount must be greater than 0" });

        var bill = await medicalDocsService.CreateBillAsync(request.PersonId, request.TotalAmount, request.Summary, request.Category, request.BillDate, request.DoctorId, request.ProviderId);
        if (bill == null)
            return StatusCode(500, new { error = "Failed to create bill" });

        return Ok(bill);
    }

    [HttpPut("bills/{id}")]
    public async Task<IActionResult> UpdateBill(long id, [FromBody] UpdateBillRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "bill", id)) return Forbid();

        if (request.TotalAmount <= 0)
            return BadRequest(new { error = "Amount must be greater than 0" });

        var success = await medicalDocsService.UpdateBillAsync(id, request.TotalAmount, request.Summary, request.Category, request.BillDate, request.DoctorId, request.ProviderId);
        if (!success) return NotFound(new { error = "Bill not found" });

        return Ok(new { success = true });
    }

    [HttpDelete("bills/{id}")]
    public async Task<IActionResult> DeleteBill(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "bill", id)) return Forbid();

        var success = await medicalDocsService.DeleteBillAsync(id);
        if (!success) return NotFound(new { error = "Bill not found" });

        return Ok(new { success = true });
    }

    [HttpPost("bills/{id}/documents")]
    public async Task<IActionResult> LinkDocumentToBill(long id, [FromBody] LinkDocumentRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "bill", id)) return Forbid();

        if (request.DocumentId <= 0)
            return BadRequest(new { error = "Document is required" });

        var success = await medicalDocsService.LinkDocumentToBillAsync(id, request.DocumentId);
        if (!success)
            return StatusCode(500, new { error = "Failed to link document" });

        return Ok(new { success = true });
    }

    [HttpDelete("bills/{billId}/documents/{docId}")]
    public async Task<IActionResult> UnlinkDocumentFromBill(long billId, long docId)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "bill", billId)) return Forbid();

        var success = await medicalDocsService.UnlinkDocumentFromBillAsync(billId, docId);
        if (!success) return NotFound(new { error = "Link not found" });

        return Ok(new { success = true });
    }

    // --- Bill Charges ---

    [HttpGet("bills/{id}/charges")]
    public async Task<IActionResult> GetBillCharges(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "bill", id)) return Forbid();

        var charges = await medicalDocsService.GetChargesAsync(id);
        return Ok(charges);
    }

    [HttpPost("bills/{id}/charges")]
    public async Task<IActionResult> CreateCharge(long id, [FromBody] CreateChargeRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "bill", id)) return Forbid();

        if (string.IsNullOrWhiteSpace(request.Description))
            return BadRequest(new { error = "Description is required" });
        if (request.Amount <= 0)
            return BadRequest(new { error = "Amount must be greater than 0" });

        var charge = await medicalDocsService.CreateChargeAsync(id, request.Description.Trim(), request.Amount);
        if (charge == null)
            return StatusCode(500, new { error = "Failed to create charge" });

        return Ok(charge);
    }

    [HttpDelete("bill-charges/{id}")]
    public async Task<IActionResult> DeleteCharge(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (!await HasResourceAccess(userId.Value, "bill-charge", id)) return Forbid();

        var success = await medicalDocsService.DeleteChargeAsync(id);
        if (!success) return NotFound(new { error = "Charge not found" });

        return Ok(new { success = true });
    }

    // --- Timeline ---

    [HttpGet("timeline")]
    public async Task<IActionResult> GetTimeline([FromQuery] long? personId = null, [FromQuery] int offset = 0, [FromQuery] int limit = 100)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (personId.HasValue && !await HasPersonAccess(userId.Value, personId.Value)) return Forbid();

        if (limit < 1 || limit > 200) limit = 100;
        if (offset < 0) offset = 0;

        var events = await medicalDocsService.GetTimelineAsync(personId, accessUserId: personId.HasValue ? null : userId, offset: offset, limit: limit);
        return Ok(events);
    }

    // --- Visit Prep ---

    [HttpGet("visit-prep")]
    public async Task<IActionResult> GetVisitPrep([FromQuery] long personId, [FromQuery] long doctorId)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        if (personId <= 0 || doctorId <= 0)
            return BadRequest(new { error = "personId and doctorId are required" });
        if (!await HasPersonAccess(userId.Value, personId)) return Forbid();

        var data = await medicalDocsService.GetVisitPrepAsync(personId, doctorId);
        return Ok(data);
    }

    [HttpPost("visit-prep/summary")]
    public async Task<IActionResult> GenerateVisitPrepSummary([FromBody] VisitPrepSummaryRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();

        if (request.PersonId <= 0 || request.DoctorId <= 0)
            return BadRequest(new { error = "personId and doctorId are required" });
        if (!await HasPersonAccess(userId.Value, request.PersonId)) return Forbid();

        var data = await medicalDocsService.GetVisitPrepAsync(request.PersonId, request.DoctorId);
        var doctor = await medicalDocsService.GetDoctorByIdAsync(request.DoctorId);
        if (doctor == null)
            return NotFound(new { error = "Doctor not found" });

        var summary = await medicalAiService.GenerateVisitPrepSummaryAsync(doctor.Name, doctor.Specialty, data);
        return Ok(new { summary });
    }

    [HttpGet("bills/summary")]
    public async Task<IActionResult> GetBillSummary([FromQuery] long? personId = null)
    {
        var userId = GetUserIdFromAuth();
        if (userId == null) return Unauthorized();
        if (!await HasMedicalAccess(userId.Value)) return Forbid();
        if (personId.HasValue && !await HasPersonAccess(userId.Value, personId.Value)) return Forbid();

        var summary = await medicalDocsService.GetBillSummaryAsync(personId, accessUserId: personId.HasValue ? null : userId);
        return Ok(summary);
    }

    // --- Auth helpers ---

    private async Task<bool> HasMedicalAccess(long userId)
    {
        return await dbExecutor.ExecuteAsync<bool>(
            "SELECT EXISTS(SELECT 1 FROM app.user_roles ur JOIN app.roles r ON ur.role_id = r.id WHERE ur.user_id = @UserId AND r.name = 'medical')",
            new { UserId = userId });
    }

    private async Task<bool> HasPersonAccess(long userId, long personId)
    {
        return await medicalDocsService.HasAccessToPersonAsync(userId, personId);
    }

    private async Task<bool> HasResourceAccess(long userId, string resourceType, long resourceId)
    {
        var personId = await medicalDocsService.GetPersonIdForResourceAsync(resourceType, resourceId);
        if (personId == null) return false;
        return await medicalDocsService.HasAccessToPersonAsync(userId, personId.Value);
    }

    private long? GetUserIdFromAuth()
    {
        var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (!string.IsNullOrEmpty(userIdClaim) && long.TryParse(userIdClaim, out var jwtUserId))
        {
            return jwtUserId;
        }

        var userIdString = HttpContext.Session.GetString("UserId");
        if (!string.IsNullOrEmpty(userIdString) && long.TryParse(userIdString, out var sessionUserId))
        {
            return sessionUserId;
        }

        return null;
    }
}

public record CreatePersonRequest(string Name, DateTime? DateOfBirth = null, string? Notes = null);
public record CreateNoteRequest(long PersonId, string Title, string? Description = null, DateTime? DocumentDate = null, string? Classification = null);
public record UpdateDocumentRequest(string? Title = null, string? Description = null, DateTime? DocumentDate = null, string? Classification = null, long? DoctorId = null);
public record CreateDoctorRequest(long PersonId, string Name, string? Specialty = null, string? Phone = null, string? Address = null, string? Notes = null);
public record CreateConditionRequest(long PersonId, string Name, DateTime? DiagnosedDate = null, string? Notes = null);
public record UpdateConditionRequest(string Name, DateTime? DiagnosedDate = null, string? Notes = null, bool IsActive = true);
public record CreatePrescriptionRequest(long PersonId, string MedicationName, string? Dosage = null, string? Frequency = null, long? DoctorId = null, DateTime? StartDate = null, string? Notes = null, string? RxNumber = null);
public record UpdatePrescriptionRequest(string MedicationName, string? Dosage = null, string? Frequency = null, long? DoctorId = null, DateTime? StartDate = null, DateTime? EndDate = null, string? Notes = null, bool IsActive = true, string? RxNumber = null);
public record CreatePickupRequest(DateTime PickupDate, string? Quantity = null, string? Pharmacy = null, decimal? Cost = null, string? Notes = null);
public record CreateProviderRequest(long PersonId, string Name, string? Notes = null);
public record UpdateProviderRequest(string Name, string? Notes = null);
public record CreateProviderPaymentRequest(decimal Amount, DateTime? PaymentDate = null, string? Description = null);
public record CreateBillRequest(long PersonId, decimal TotalAmount, string? Summary = null, string? Category = null, DateTime? BillDate = null, long? DoctorId = null, long? ProviderId = null);
public record UpdateBillRequest(decimal TotalAmount, string? Summary = null, string? Category = null, DateTime? BillDate = null, long? DoctorId = null, long? ProviderId = null);
public record LinkDocumentRequest(long DocumentId);
public record CreateChargeRequest(string Description, decimal Amount);
public record ProcessBatchRequest(List<long> DocumentIds);
public record VisitPrepSummaryRequest(long PersonId, long DoctorId);
public record GrantAccessRequest(string Username);
