"""Diagnosis API routes."""
from bson import ObjectId
from flask import Blueprint, jsonify, request
from pydantic import ValidationError

from app.db import Database
from app.models.diagnosis import Diagnosis
from app.repositories import diagnosis_repository
from app.routes.access import ensure_can_access_patient_data, ensure_can_create_for_patient
from app.routes.auth import auth_required

diagnoses_bp = Blueprint('diagnoses_api', __name__)


def _populate_diagnosis(diagnosis_dict: dict) -> dict:
    """Populate a diagnosis dict with disease details and related prescriptions."""
    db = Database.get_db()
    icd_code = diagnosis_dict.get('icdCode', '')
    diagnosis_id = diagnosis_dict.get('_id')
    patient_id = diagnosis_dict.get('patientId')

    # --- Populate disease from diseases collection (match by icdCode) ---
    disease = db.get_collection('diseases').find_one({'icdCode': icd_code})
    if not disease:
        # Try base code (e.g., H52.6 → H52)
        base_code = icd_code.split('.')[0]
        disease = db.get_collection('diseases').find_one({'icdCode': base_code})

    if disease:
        diagnosis_dict['disease'] = {
            '_id': str(disease['_id']),
            'icdCode': disease.get('icdCode', ''),
            'name': disease.get('name', ''),
            'bodyRegion': disease.get('bodyRegion', ''),
            'description': disease.get('description', ''),
            'symptoms': disease.get('symptoms', ''),
            'commonTreatments': disease.get('commonTreatments', ''),
        }

    # --- Populate related prescriptions (linked via diagnosisId) ---
    prescriptions_cursor = db.get_collection('prescriptions').find({
        'diagnosisId': ObjectId(str(diagnosis_id)) if diagnosis_id else None
    })
    related_prescriptions = []
    for p in prescriptions_cursor:
        pres = {
            '_id': str(p['_id']),
            'prescriptionId': p.get('prescriptionId', ''),
            'dosage': p.get('dosage', ''),
            'frequency': p.get('frequency', ''),
            'startDate': str(p.get('startDate', '')),
            'endDate': str(p.get('endDate', '')),
        }
        # Populate medication for this prescription
        med_id = p.get('medicationId')
        if med_id:
            med = db.get_collection('medications').find_one({'_id': ObjectId(str(med_id))})
            if med:
                pres['medication'] = {
                    '_id': str(med['_id']),
                    'genericName': med.get('genericName', ''),
                    'brandName': med.get('brandName', ''),
                    'pharmClass': med.get('pharmClass', ''),
                    'adverseReactions': med.get('adverseReactions', ''),
                }
        related_prescriptions.append(pres)

    diagnosis_dict['relatedPrescriptions'] = related_prescriptions
    return diagnosis_dict


@diagnoses_bp.route('/diagnoses', methods=['POST'])
@auth_required
def create_diagnosis_endpoint():
    """Create a diagnosis.
    ---
    tags:
        - Diagnoses
    security:
        - BearerAuth: []
    requestBody:
        required: true
        content:
            application/json:
                schema:
                    $ref: '#/components/schemas/DiagnosisCreateRequest'
    responses:
        201:
            description: Diagnosis created
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/DiagnosisResponse'
        400:
            description: Validation or payload error
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
        401:
            description: Unauthorized
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
        403:
            description: Forbidden
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
    """
    payload = request.get_json(silent=True) or {}
    patient_id = str(payload.get('patientId', ''))
    create_err = ensure_can_create_for_patient(patient_id)
    if create_err:
        return create_err

    try:
        diagnosis = Diagnosis.model_validate(payload)
    except ValidationError as exc:
        return jsonify({'error': 'ValidationError', 'detail': str(exc)}), 400

    created = diagnosis_repository.create(diagnosis)
    return jsonify(created.model_dump(by_alias=True, mode='json')), 201


@diagnoses_bp.route('/diagnoses/<diagnosis_id>', methods=['GET'])
@auth_required
def get_diagnosis_by_id_endpoint(diagnosis_id: str):
    """Get a diagnosis by ID.
    ---
    tags:
        - Diagnoses
    security:
        - BearerAuth: []
    parameters:
        - in: path
          name: diagnosis_id
          required: true
          schema:
            type: string
    responses:
        200:
            description: Diagnosis found
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/DiagnosisResponse'
        401:
            description: Unauthorized
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
        403:
            description: Forbidden
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
        404:
            description: Diagnosis not found
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
    """
    diagnosis = diagnosis_repository.find_by_id(diagnosis_id)
    if not diagnosis:
        return jsonify({'error': 'NotFound'}), 404

    _, err = ensure_can_access_patient_data(str(diagnosis.patientId))
    if err:
        return err

    result = _populate_diagnosis(diagnosis.model_dump(by_alias=True, mode='json'))
    return jsonify(result)


@diagnoses_bp.route('/diagnoses', methods=['GET'])
@auth_required
def list_diagnoses_by_patient_endpoint():
    """List diagnoses by patient ID query parameter.
    ---
    tags:
        - Diagnoses
    security:
        - BearerAuth: []
    parameters:
        - in: query
          name: patientId
          required: true
          schema:
            type: string
    responses:
        200:
            description: Diagnoses list
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/DiagnosisResponseList'
        400:
            description: Missing or invalid patientId
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
        401:
            description: Unauthorized
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
        403:
            description: Forbidden
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
    """
    patient_id = request.args.get('patientId')
    if not patient_id:
        return jsonify({'error': 'MissingQueryParam', 'param': 'patientId'}), 400

    _, err = ensure_can_access_patient_data(patient_id)
    if err:
        return err

    try:
        diagnoses = diagnosis_repository.find_by_patient_id(patient_id)
    except ValueError as exc:
        return jsonify({'error': 'InvalidPatientId', 'detail': str(exc)}), 400

    return jsonify([
        _populate_diagnosis(d.model_dump(by_alias=True, mode='json'))
        for d in diagnoses
    ])


@diagnoses_bp.route('/diagnoses/<diagnosis_id>', methods=['PATCH'])
@auth_required
def update_diagnosis_endpoint(diagnosis_id: str):
    """Update a diagnosis record.
    ---
    tags:
        - Diagnoses
    security:
        - BearerAuth: []
    parameters:
        - in: path
          name: diagnosis_id
          required: true
          schema:
            type: string
    requestBody:
        required: true
        content:
            application/json:
                schema:
                    $ref: '#/components/schemas/DiagnosisUpdateRequest'
    responses:
        200:
            description: Diagnosis updated
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/DiagnosisResponse'
        400:
            description: No updatable fields or invalid ID
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
        401:
            description: Unauthorized
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
        403:
            description: Forbidden
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
        404:
            description: Diagnosis not found
            content:
                application/json:
                    schema:
                        $ref: '#/components/schemas/ErrorResponse'
    """
    existing = diagnosis_repository.find_by_id(diagnosis_id)
    if not existing:
        return jsonify({'error': 'NotFound'}), 404

    _, err = ensure_can_access_patient_data(str(existing.patientId))
    if err:
        return err

    updates = request.get_json(silent=True) or {}
    forbidden = {'_id', 'id', 'created_at', 'updated_at'}
    filtered_updates = {k: v for k, v in updates.items() if k not in forbidden}
    if not filtered_updates:
        return jsonify({'error': 'NoUpdatableFields'}), 400

    try:
        updated = diagnosis_repository.update(diagnosis_id, filtered_updates)
    except ValueError as exc:
        return jsonify({'error': 'InvalidDiagnosisId', 'detail': str(exc)}), 400

    if not updated:
        return jsonify({'error': 'NotFound'}), 404

    return jsonify(updated.model_dump(by_alias=True, mode='json'))


@diagnoses_bp.route('/diagnoses/<diagnosis_id>', methods=['DELETE'])
@auth_required
def delete_diagnosis_endpoint(diagnosis_id: str):
    """Delete a diagnosis record.
    ---
    tags:
        - Diagnoses
    security:
        - BearerAuth: []
    parameters:
        - in: path
          name: diagnosis_id
          required: true
          schema:
            type: string
    responses:
        204:
            description: Diagnosis deleted
        401:
            description: Unauthorized
        403:
            description: Forbidden
        404:
            description: Diagnosis not found
    """
    existing = diagnosis_repository.find_by_id(diagnosis_id)
    if not existing:
        return jsonify({'error': 'NotFound'}), 404

    _, err = ensure_can_access_patient_data(str(existing.patientId))
    if err:
        return err

    success = diagnosis_repository.delete(diagnosis_id)
    if not success:
        return jsonify({'error': 'NotFound'}), 404

    return '', 204
