"""Multimedia training assets generated for a course-plan material.

Each material (a reading text, listening script, vocab list, etc.) can
have one or more linked media rows: an MP3 narration of a listening
script, a DALL-E illustration for a vocab card, an MP4 slideshow video
combining the two, and so on. Bytes live on ``ir.attachment`` so the
existing filestore + access controls + URL serving (``/web/content``)
all work for free.

Media generation is triggered explicitly through REST endpoints — we
never generate inline during plan creation because each call has a
non-trivial latency and cost.
"""

import logging

from odoo import api, fields, models

_logger = logging.getLogger(__name__)


MEDIA_KIND_SELECTION = [
    ('audio', 'Audio'),
    ('image', 'Image'),
    ('video', 'Video'),
]

MEDIA_PROVIDER_SELECTION = [
    # ── Paid providers ──
    ('polly', 'AWS Polly'),
    ('elevenlabs', 'ElevenLabs'),
    ('openai_image', 'OpenAI (DALL-E)'),
    ('elai', 'Elai.io'),
    # ── Free fallbacks (Phase 24.1) ──
    ('pillow', 'Pillow placeholder (offline)'),
    ('unsplash', 'Unsplash Source (free)'),
    ('gtts', 'gTTS (free TTS)'),
    ('silent', 'Silent stub'),
    ('static', 'Static image as video'),
    ('mock', 'Mock'),
    # ── Composers / manual ──
    ('ffmpeg', 'ffmpeg (slideshow)'),
    ('manual', 'Manual upload'),
    # Sentinel value used while the chain is still resolving — written
    # transiently by MediaService.create() and overwritten with the
    # successful provider name once a fallback step succeeds.
    ('auto', 'Auto (fallback chain)'),
]

MEDIA_STATUS_SELECTION = [
    ('queued', 'Queued'),
    ('generating', 'Generating'),
    ('ready', 'Ready'),
    ('failed', 'Failed'),
]


class CoursePlanMedia(models.Model):
    _name = 'encoach.course.plan.media'
    _description = 'Course Plan Multimedia Asset'
    _order = 'kind, id desc'

    plan_id = fields.Many2one(
        'encoach.course.plan', required=True, ondelete='cascade', index=True,
    )
    week_id = fields.Many2one(
        'encoach.course.plan.week', ondelete='cascade', index=True,
    )
    material_id = fields.Many2one(
        'encoach.course.plan.material', ondelete='cascade', index=True,
    )

    kind = fields.Selection(MEDIA_KIND_SELECTION, required=True)
    provider = fields.Selection(MEDIA_PROVIDER_SELECTION, required=True)
    title = fields.Char()
    source_text = fields.Text(
        help='The text fed to the generator (TTS script, image prompt, etc.).',
    )
    voice = fields.Char()
    language = fields.Char(default='en-GB')
    style = fields.Char(help='DALL-E style or video template label.')

    attachment_id = fields.Many2one(
        'ir.attachment', ondelete='set null', string='Binary',
    )
    mime_type = fields.Char()
    size_bytes = fields.Integer()
    duration_seconds = fields.Float()
    width = fields.Integer()
    height = fields.Integer()
    download_url = fields.Char(
        compute='_compute_media_urls', store=False,
        help='Authenticated REST URL that serves the binary as an attachment '
             '(``Content-Disposition: attachment``). Frontend appends '
             '``?token=<jwt>`` for download buttons.',
    )
    preview_url = fields.Char(
        compute='_compute_media_urls', store=False,
        help='Authenticated REST URL that serves the binary inline so it can '
             'be used as the ``src`` for ``<img>`` / ``<audio>`` / ``<video>`` '
             'elements. Frontend appends ``?token=<jwt>``.',
    )

    status = fields.Selection(MEDIA_STATUS_SELECTION, default='queued')
    error = fields.Char()
    cost_cents = fields.Integer(
        default=0,
        help='Approximate provider cost in USD cents at generation time, '
             'best-effort accounting for budget caps.',
    )

    @api.depends('attachment_id')
    def _compute_media_urls(self):
        # Both URLs hit the same JWT-protected streaming endpoint exposed by
        # ``encoach_ai_course.controllers.course_plan.CoursePlanController.
        # stream_media``. The endpoint accepts the JWT either as a Bearer
        # header (REST clients) or as a ``?token=`` query param so plain
        # ``<img>`` / ``<audio>`` / ``<video>`` tags can render the asset
        # without us proxying every request through fetch + blob URLs.
        # ``download=1`` only changes the Content-Disposition: attachment
        # header so the same route serves both inline previews and explicit
        # downloads.
        for rec in self:
            if not rec.id:
                rec.download_url = ''
                rec.preview_url = ''
                continue
            base = f'/api/ai/course-plan/media/{rec.id}/raw'
            rec.preview_url = base
            rec.download_url = f'{base}?download=1'

    def to_api_dict(self):
        self.ensure_one()
        return {
            'id': self.id,
            'plan_id': self.plan_id.id,
            'week_id': self.week_id.id if self.week_id else None,
            'material_id': self.material_id.id if self.material_id else None,
            'kind': self.kind or '',
            'provider': self.provider or '',
            'title': self.title or '',
            'voice': self.voice or '',
            'language': self.language or '',
            'style': self.style or '',
            'mime_type': self.mime_type or '',
            'size_bytes': self.size_bytes or 0,
            'duration_seconds': self.duration_seconds or 0.0,
            'width': self.width or 0,
            'height': self.height or 0,
            'attachment_id': self.attachment_id.id if self.attachment_id else None,
            'download_url': self.download_url,
            'preview_url': self.preview_url,
            'status': self.status or 'queued',
            'error': self.error or '',
            'cost_cents': self.cost_cents or 0,
            'created_at': self.create_date.isoformat() if self.create_date else None,
        }
