"""
Video call recording service using Agora Cloud Recording.
Recording starts on mutual connection (both doctor + patient) and stops when either leaves.
All sessions linked to the same appointment; multiple connect/disconnect cycles produce multiple recordings.
Does not affect other modules: all logic is isolated, failures are logged and swallowed.
"""
import base64
import time
import requests
from datetime import datetime
from flask import current_app
from app import db
from app.models import Appointment, VideoCallRecording

# Recorder UID must not conflict with doctor/patient UIDs (typically < 1000)
RECORDER_UID = 999999
AGORA_API_BASE = 'https://api.agora.io/v1/apps'


def _is_enabled():
    """Check if recording is enabled and Agora + storage credentials are present."""
    if not current_app.config.get('AGORA_ENABLE_RECORDING'):
        return False
    cid = current_app.config.get('AGORA_CUSTOMER_ID', '').strip()
    secret = current_app.config.get('AGORA_CUSTOMER_SECRET', '').strip()
    bucket = current_app.config.get('AGORA_RECORDING_STORAGE_BUCKET', '').strip()
    access_key = current_app.config.get('AGORA_RECORDING_STORAGE_ACCESS_KEY', '').strip()
    secret_key = current_app.config.get('AGORA_RECORDING_STORAGE_SECRET_KEY', '').strip()
    if not cid or not secret or not bucket or not access_key or not secret_key:
        return False
    return True


def _auth_header():
    """Basic auth: base64(CustomerId:CustomerSecret)."""
    cid = current_app.config.get('AGORA_CUSTOMER_ID', '')
    secret = current_app.config.get('AGORA_CUSTOMER_SECRET', '')
    creds = base64.b64encode(f'{cid}:{secret}'.encode()).decode()
    return {'Authorization': f'Basic {creds}'}


def _get_recorder_token(appointment_id):
    """Build RTC token for recorder UID."""
    from agora_token_builder import RtcTokenBuilder
    app_id = current_app.config.get('AGORA_APP_ID')
    cert = current_app.config.get('AGORA_APP_CERTIFICATE')
    if not app_id or not cert:
        return None
    channel = f'appointment_{appointment_id}'
    # Token valid 24h
    privilege_ts = int(time.time()) + 86400
    return RtcTokenBuilder.buildTokenWithUid(
        app_id, cert, channel, RECORDER_UID, 1, privilege_ts
    )


def try_start_recording(appointment_id):
    """
    Start recording when mutual connection is detected.
    No-op if disabled, not configured, or appointment invalid.
    Never raises; logs errors.
    """
    if not _is_enabled():
        return

    try:
        appointment = Appointment.query.get(appointment_id)
        if not appointment:
            return
        if appointment.appointment_type != 'video':
            return
        if not (appointment.doctor_joined_video and appointment.patient_joined_video):
            return
        # Already recording for this appointment?
        active = VideoCallRecording.query.filter_by(
            appointment_id=appointment_id, status='recording'
        ).first()
        if active:
            return

        app_id = current_app.config.get('AGORA_APP_ID')
        channel = f'appointment_{appointment_id}'
        token = _get_recorder_token(appointment_id)
        if not token:
            current_app.logger.warning('Recording: missing token for appt %s', appointment_id)
            return

        # Storage config
        vendor = current_app.config.get('AGORA_RECORDING_STORAGE_VENDOR', 1)
        region = current_app.config.get('AGORA_RECORDING_STORAGE_REGION', 0)
        bucket = current_app.config.get('AGORA_RECORDING_STORAGE_BUCKET')
        access_key = current_app.config.get('AGORA_RECORDING_STORAGE_ACCESS_KEY')
        secret_key = current_app.config.get('AGORA_RECORDING_STORAGE_SECRET_KEY')
        storage = {
            'vendor': vendor,
            'region': region,
            'bucket': bucket,
            'accessKey': access_key,
            'secretKey': secret_key,
        }

        # 1. Acquire resource
        acquire_url = f'{AGORA_API_BASE}/{app_id}/cloud_recording/acquire'
        acquire_body = {
            'cname': channel,
            'uid': str(RECORDER_UID),
            'clientRequest': {},
        }
        r = requests.post(
            acquire_url, json=acquire_body, headers=_auth_header(), timeout=10
        )
        if r.status_code != 200:
            current_app.logger.warning('Recording acquire failed for appt %s: %s', appointment_id, r.text)
            return
        resource_id = r.json().get('resourceId')
        if not resource_id:
            return

        # 2. Start recording (composite/mix mode)
        start_url = f'{AGORA_API_BASE}/{app_id}/cloud_recording/resourceid/{resource_id}/mode/mix/start'
        start_body = {
            'cname': channel,
            'uid': str(RECORDER_UID),
            'clientRequest': {
                'token': token,
                'storageConfig': storage,
                'recordingConfig': {'channelType': 0},
            },
        }
        r2 = requests.post(
            start_url, json=start_body, headers=_auth_header(), timeout=10
        )
        if r2.status_code != 200:
            current_app.logger.warning('Recording start failed for appt %s: %s', appointment_id, r2.text)
            return
        sid = r2.json().get('sid')
        if not sid:
            return

        rec = VideoCallRecording(
            appointment_id=appointment_id,
            agora_resource_id=resource_id,
            agora_sid=sid,
            status='recording',
        )
        db.session.add(rec)
        db.session.commit()
        current_app.logger.info('Recording started for appointment %s sid=%s', appointment_id, sid)
    except Exception as e:
        db.session.rollback()
        current_app.logger.warning('Recording start error for appt %s: %s', appointment_id, str(e))


def stop_recording(appointment_id):
    """
    Stop active recording when call ends.
    Saves file info from stop response; never raises.
    """
    if not _is_enabled():
        return

    try:
        active = VideoCallRecording.query.filter_by(
            appointment_id=appointment_id, status='recording'
        ).first()
        if not active:
            return

        app_id = current_app.config.get('AGORA_APP_ID')
        channel = f'appointment_{appointment_id}'
        stop_url = (
            f'{AGORA_API_BASE}/{app_id}/cloud_recording/resourceid/{active.agora_resource_id}'
            f'/sid/{active.agora_sid}/mode/mix/stop'
        )
        stop_body = {
            'uid': str(RECORDER_UID),
            'cname': channel,
            'clientRequest': {},
        }
        r = requests.post(
            stop_url, json=stop_body, headers=_auth_header(), timeout=15
        )
        active.status = 'stopping'
        active.ended_at = datetime.utcnow()
        if r.status_code == 200:
            data = r.json()
            # Extract file info from fileList (filename may be storage key; full URL depends on storage config)
            flist = data.get('resource', {}).get('fileList', []) or []
            if isinstance(flist, dict):
                flist = flist.get('filename', []) or []
            if not isinstance(flist, list):
                flist = [flist] if flist else []
            if flist:
                f0 = flist[0] if isinstance(flist[0], dict) else {'filename': str(flist[0])}
                active.file_path = f0.get('filename') or f0.get('fileList')
                active.file_url = active.file_path  # May need full URL from storage; user configures
                active.duration_seconds = f0.get('duration')
            active.status = 'ready'
            db.session.commit()
            current_app.logger.info('Recording stopped for appointment %s', appointment_id)
        else:
            active.status = 'failed'
            active.error_message = r.text[:500]
            db.session.commit()
            current_app.logger.warning('Recording stop failed for appt %s: %s', appointment_id, r.text)
    except Exception as e:
        db.session.rollback()
        current_app.logger.warning('Recording stop error for appt %s: %s', appointment_id, str(e))


def get_recordings_for_appointment(appointment_id):
    """Return all recordings for an appointment (for admin)."""
    return VideoCallRecording.query.filter_by(appointment_id=appointment_id).order_by(
        VideoCallRecording.started_at.desc()
    ).all()
