//! 顶层入口:[`Scheduler`]。

use std::sync::Arc;

use mineral_channel_core::MusicChannel;
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};

use crate::event::TaskEvent;
use crate::handle::TaskHandle;
use crate::id::{Priority, TaskId};
use crate::kind::ChannelFetchKindTag;
use crate::kind::TaskKind;
use crate::lane::Lane;
use crate::lanes::channel_fetch::{ChannelFetchLane, Job as ChannelFetchJob};
use crate::lanes::playlist_write::{Job as PlaylistWriteJob, PlaylistWriteLane};
use crate::ongoing::{Bind, Ongoing};

/// 进程内任务调度器入口。`Clone` 廉价(`Arc<Inner>`)。
#[derive(Clone)]
pub struct Scheduler {
    /// 共享内部状态(中央 ongoing / 事件 buffer / 各 lane)。
    inner: Arc<Inner>,
}

/// Scheduler 的真实状态,被 `Arc` 包起来共享。
struct Inner {
    /// 中央 ongoing 状态(任务表 + dedup 索引)。
    ongoing: Arc<Ongoing>,

    /// 中央事件 buffer:lane worker 推、UI 主循环 [`Scheduler::drain_events`] 拉。
    events: Arc<Mutex<Vec<TaskEvent>>>,

    /// ChannelFetch lane(per-channel worker 池)。
    channel_fetch: ChannelFetchLane,

    /// PlaylistWrite lane(per-source 单 worker 串行)。
    playlist_write: PlaylistWriteLane,
}

/// `Scheduler::snapshot` 的返回:当前 running 数与按 lane / kind 的拆分。
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Snapshot {
    /// 全部 lane 的 running 任务总数。
    pub running: usize,

    /// 每个 lane 的 running 任务计数(无任务的 lane 不出现)。
    pub by_lane: FxHashMap<Lane, usize>,

    /// `ChannelFetch` 任务按 [`ChannelFetchKindTag`] 细分(其它种类不在此 map)。
    /// UI 用它做「`pl:1 tr:2 song:1 lyr:0 ♥:0`」级别的拆分显示。
    pub by_kind: FxHashMap<ChannelFetchKindTag, usize>,
}

impl Scheduler {
    /// 用一组已构造好的 channel 初始化 scheduler。每个 channel 起对应 lane 的 worker 池。
    ///
    /// # Params:
    ///   - `channels`: 注入的所有 channel(用于 ChannelFetch lane 路由)
    ///   - `workers_per_channel`: 每个 channel 的任务 worker 数(配置 `daemon.channel_workers_per`)
    pub fn new(channels: &[Arc<dyn MusicChannel>], workers_per_channel: usize) -> Self {
        let ongoing = Arc::new(Ongoing::new());
        let events = Arc::new(Mutex::new(Vec::<TaskEvent>::new()));
        let channel_fetch =
            ChannelFetchLane::spawn(channels, &ongoing, &events, workers_per_channel);
        let playlist_write = PlaylistWriteLane::spawn(channels, &ongoing, &events);
        Self {
            inner: Arc::new(Inner {
                ongoing,
                events,
                channel_fetch,
                playlist_write,
            }),
        }
    }

    /// 提交一个任务。
    ///
    /// # 行为
    ///   - 若已存在同 `dedup_key` 的进行中任务且新优先级 ≤ 旧优先级,直接返回旧 handle 的副本。
    ///   - 若已存在但新优先级更高,旧任务被 cancel,新任务进对应优先级队列。
    ///   - 否则按种类路由到对应 lane。
    pub fn submit(&self, kind: TaskKind, priority: Priority) -> TaskHandle {
        match self.inner.ongoing.bind(kind.clone(), priority) {
            Bind::Shared(handle) => {
                mineral_log::debug!(target: "scheduler", kind = ?kind, "submit deduped to existing task");
                handle
            }
            Bind::Fresh {
                id,
                handle,
                done_tx,
            } => {
                mineral_log::debug!(target: "scheduler", task_id = ?id, kind = ?kind, priority = ?priority, "submit new task");
                self.dispatch(id, kind, priority, &handle, done_tx);
                handle
            }
        }
    }

    /// 把新建任务路由到对应 lane(目前只有 ChannelFetch)。
    fn dispatch(
        &self,
        id: TaskId,
        kind: TaskKind,
        priority: Priority,
        handle: &TaskHandle,
        done_tx: tokio::sync::oneshot::Sender<crate::outcome::TaskOutcome>,
    ) {
        match kind {
            TaskKind::ChannelFetch(k) => {
                let source = k.source();
                self.inner.channel_fetch.dispatch(
                    source,
                    priority,
                    ChannelFetchJob {
                        id,
                        kind: k,
                        priority,
                        cancel: handle.cancel.clone(),
                        done_tx,
                    },
                );
            }
            TaskKind::PlaylistWrite(op) => {
                let source = op.target_source();
                self.inner.playlist_write.dispatch(
                    source,
                    PlaylistWriteJob {
                        id,
                        op,
                        cancel: handle.cancel.clone(),
                        done_tx,
                    },
                    &self.inner.events,
                );
            }
        }
    }

    /// 单个取消。命中既存任务时 cancel 其 token;worker 在下个 await 点感知。
    pub fn cancel(&self, id: TaskId) {
        self.inner.ongoing.cancel(id);
    }

    /// 批量取消满足谓词的任务。常用于"输入又变了,砍掉所有 Search"等场景。
    pub fn cancel_where<F>(&self, pred: F)
    where
        F: Fn(&TaskKind) -> bool + Send + Sync,
    {
        self.inner.ongoing.cancel_where(&pred);
    }

    /// 从中央事件 buffer 拿走全部已积攒的事件,buffer 清空。UI 主循环 tick 时调一次。
    pub fn drain_events(&self) -> Vec<TaskEvent> {
        std::mem::take(&mut *self.inner.events.lock())
    }

    /// 当前调度状态快照。
    pub fn snapshot(&self) -> Snapshot {
        let counts = self.inner.ongoing.snapshot();
        Snapshot {
            running: counts.running,
            by_lane: counts.by_lane,
            by_kind: counts.by_kind,
        }
    }
}
