"""Shared attachment-filing logic for every intake channel (email today,
Telegram, WhatsApp later). Once a channel-specific module has identified the
User behind an incoming message, this is what decides where the attachments
actually go:

- exactly one managed profile → file directly (no ambiguity)
- more than one managed profile → hold pending, caregiver assigns from /perfiles/pendientes
- no profile yet, or no valid attachment → log a rejection, nothing to file

Keeping this here (instead of duplicated per channel) means a new channel only
has to identify the sender and extract raw (filename, bytes) attachments —
the filing/ambiguity rules stay in one place.
"""
import os
import uuid

from flask import current_app
from werkzeug.utils import secure_filename

from app.extensions import db
from app.models import Document, IntakeEvent

EXT_TO_MIME = {
    "pdf": "application/pdf",
    "png": "image/png",
    "jpg": "image/jpeg",
    "jpeg": "image/jpeg",
}


def allowed_filename(filename: str) -> bool:
    if not filename or "." not in filename:
        return False
    ext = filename.rsplit(".", 1)[-1].lower()
    return ext in current_app.config["ALLOWED_EXTENSIONS"]


def save_attachment(folder: str, filename: str, content: bytes) -> str:
    os.makedirs(folder, exist_ok=True)
    safe_name = secure_filename(filename)
    stored_name = f"{uuid.uuid4().hex}_{safe_name}"
    storage_path = os.path.join(folder, stored_name)
    with open(storage_path, "wb") as f:
        f.write(content)
    return storage_path


def file_attachments_for_user(
    user, attachments: list[tuple[str, bytes]], channel: str,
    sender_identifier: str = None, subject: str = None,
) -> list[IntakeEvent]:
    """attachments: list of (filename, content_bytes), already size/extension-filtered
    by the caller if it wants finer control — extension is re-checked here regardless.

    Returns the IntakeEvent(s) created (added to the session, not committed —
    the caller controls the transaction).
    """
    patients = user.patients
    valid_attachments = [(f, c) for f, c in attachments if allowed_filename(f)]

    if not valid_attachments:
        event = IntakeEvent(
            channel=channel,
            status=IntakeEvent.STATUS_REJECTED_ATTACHMENT,
            sender_identifier=sender_identifier,
            subject=subject,
            user_id=user.id,
            patient_id=patients[0].id if len(patients) == 1 else None,
            notes="No traía adjuntos en un formato permitido (PDF, JPG, PNG).",
        )
        db.session.add(event)
        return [event]

    # Exactly one managed profile: no ambiguity, file it directly.
    if len(patients) == 1:
        patient = patients[0]
        patient_folder = os.path.join(current_app.config["UPLOAD_FOLDER"], str(patient.id))

        events = []
        for filename, content in valid_attachments:
            storage_path = save_attachment(patient_folder, filename, content)
            ext = filename.rsplit(".", 1)[-1].lower()

            document = Document(
                patient_id=patient.id,
                original_filename=secure_filename(filename),
                storage_path=storage_path,
                mime_type=EXT_TO_MIME.get(ext, "application/octet-stream"),
                status=Document.STATUS_PENDING,
                source_channel=channel,
            )
            db.session.add(document)
            db.session.flush()

            event = IntakeEvent(
                channel=channel, status=IntakeEvent.STATUS_MATCHED,
                sender_identifier=sender_identifier, subject=subject,
                user_id=user.id, patient_id=patient.id, document_id=document.id,
            )
            db.session.add(event)
            events.append(event)
        return events

    # This account hasn't created any patient profile yet: nothing to file
    # into, and nowhere useful to hold the attachment either.
    if not patients:
        event = IntakeEvent(
            channel=channel,
            status=IntakeEvent.STATUS_REJECTED_ATTACHMENT,
            sender_identifier=sender_identifier,
            subject=subject,
            user_id=user.id,
            notes="Esta cuenta todavía no tiene ningún perfil de paciente creado.",
        )
        db.session.add(event)
        return [event]

    # More than one managed profile: we can't tell which one this is for from
    # the sender alone, so hold the file for the caregiver to assign from
    # /perfiles/pendientes.
    pending_folder = os.path.join(current_app.config["UPLOAD_FOLDER"], "_pending", str(user.id))

    events = []
    for filename, content in valid_attachments:
        storage_path = save_attachment(pending_folder, filename, content)
        event = IntakeEvent(
            channel=channel,
            status=IntakeEvent.STATUS_AMBIGUOUS_PATIENT,
            sender_identifier=sender_identifier,
            subject=subject,
            user_id=user.id,
            pending_storage_path=storage_path,
            pending_original_filename=secure_filename(filename),
            notes="Esta cuenta administra varios perfiles; falta indicar a cuál pertenece.",
        )
        db.session.add(event)
        events.append(event)
    return events
