/*
    This file is part of jellything (https://codeberg.org/metamuffin/jellything)
    which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
    Copyright (C) 2026 metamuffin <metamuffin.org>
*/

use crate::{
    plugins::{ImportPlugin, PluginContext, PluginInfo},
    source_rank::ObjectImportSourceExt,
};
use anyhow::Result;
use jellycommon::{jellyobject::OBB, *};
use jellydb::RowNum;
use jellyremuxer::matroska::{Segment, TrackType};
use std::path::Path;

pub struct MediaInfo;
impl ImportPlugin for MediaInfo {
    fn info(&self) -> PluginInfo {
        PluginInfo {
            name: "media-info",
            tag: MSOURCE_MEDIA,
            handle_media: true,
            ..Default::default()
        }
    }
    fn media(&self, ct: &PluginContext, row: RowNum, path: &Path, seg: &Segment) -> Result<()> {
        let size = path.metadata()?.len();
        ct.ic.db.transaction(&mut |txn| {
            let mut node = txn.get(row)?.unwrap();
            if let Some(tracks) = &seg.tracks {
                node = node.extend_object(
                    NO_TRACK,
                    TR_SOURCE.0,
                    &tracks
                        .entries
                        .iter()
                        .map(|t| {
                            let mut track = OBB::new();
                            track.push(TR_CODEC, &t.codec_id);
                            track.push(TR_LANGUAGE, &t.language);
                            if let Some(name) = &t.name {
                                track.push(TR_NAME, name);
                            }
                            track.push(
                                TR_KIND,
                                match t.track_type {
                                    TrackType::Audio => TRKIND_AUDIO,
                                    TrackType::Video => TRKIND_VIDEO,
                                    TrackType::Subtitle => TRKIND_TEXT,
                                    _ => TRKIND_UNKNOWN,
                                },
                            );
                            if let Some(v) = &t.video {
                                track.push(TR_PIXEL_WIDTH, v.pixel_width as u32);
                                track.push(TR_PIXEL_HEIGHT, v.pixel_height as u32);
                                if let Some(fr) = v.frame_rate {
                                    track.push(TR_RATE, fr);
                                }
                            }
                            if let Some(a) = &t.audio {
                                track.push(TR_CHANNELS, a.channels as u32);
                                track.push(TR_RATE, a.sampling_frequency);
                                if let Some(d) = a.bit_depth {
                                    track.push(TR_BIT_DEPTH, d as u32);
                                }
                            }

                            let source = OBB::new()
                                .with(TRSOURCE_LOCAL_PATH, &path.to_string_lossy())
                                .with(TRSOURCE_LOCAL_TRACKNUM, t.track_number)
                                .finish();
                            track.push(TR_SOURCE, &source);

                            track.finish()
                        })
                        .collect::<Vec<_>>(),
                );
            }

            if let Some(chapters) = &seg.chapters {
                node = node.extend_object(
                    NO_CHAPTER,
                    CH_NAME.0,
                    &chapters
                        .edition_entries
                        .iter()
                        .flat_map(|e| &e.chapter_atoms)
                        .map(|cha| {
                            let mut chapter = OBB::new();
                            chapter.push(CH_START, cha.time_start as f64 * 1e-9);
                            if let Some(end) = cha.time_end {
                                chapter.push(CH_END, end as f64 * 1e-9);
                            }
                            if let Some(display) = cha.displays.iter().next() {
                                chapter.push(CH_NAME, &display.string);
                            }
                            chapter.finish()
                        })
                        .collect::<Vec<_>>(),
                );
            }

            let runtime = fix_invalid_runtime(
                seg.info.duration.unwrap_or_default() * seg.info.timestamp_scale as f64 * 1e-9,
            );
            node = node.insert_s(ct.is, NO_DURATION, runtime);
            node = node.insert_s(ct.is, NO_STORAGE_SIZE, size);

            txn.update(row, node)?;

            Ok(())
        })
    }
}

fn fix_invalid_runtime(d: f64) -> f64 {
    match d {
        // Broken durations found experimentally
        359999.999 | 359999.000 | 86399.999 | 86399.99900000001 => 0.,
        x => x,
    }
}
