use anyhow::{Result, anyhow}; // added Context/bail per style (harmless)
use std::{
    path::Path,
    time::{SystemTime, UNIX_EPOCH},
};

use chrono::{DateTime, NaiveDate};

/// Convert unix seconds -> "YYYYMMDDhhmmss"
pub fn unix_secs_to_yyyymmddhhmmss(ts: i64) -> Result<String> {
    // Use the fallible from_timestamp_opt and return an error instead of unwrapping
    let dt = DateTime::from_timestamp(ts, 0)
        .ok_or_else(|| anyhow!("invalid unix seconds timestamp: {}", ts))?;
    Ok(dt.format("%Y%m%d%H%M%S").to_string())
}

/// days since UNIX epoch (1970-01-01) for a civil date
pub fn days_from_civil(year: i32, month: u32, day: u32) -> Result<i64> {
    let date = NaiveDate::from_ymd_opt(year, month, day)
        .ok_or_else(|| anyhow!("invalid date: {:04}-{:02}-{:02}", year, month, day))?;
    let ndt = date.and_hms_opt(0, 0, 0).ok_or_else(|| {
        anyhow!(
            "invalid midnight for date: {:04}-{:02}-{:02}",
            year,
            month,
            day
        )
    })?;
    Ok(ndt
        .and_utc()
        .timestamp()
        .checked_div(86400)
        .ok_or_else(|| anyhow!("failed to compute days"))?)
}

/// 解析文件名中的时间字符串 "YYYYMMDDhhmmss" 为 UNIX 秒
pub(crate) fn parse_filename_time_to_unix(s: &str) -> Result<i64> {
    if s.len() != 14 {
        return Err(anyhow!("expected YYYYMMDDhhmmss (14 digits)"));
    }
    let year: i32 = s[0..4].parse().map_err(|_| anyhow!("bad year"))?;
    let month: u32 = s[4..6].parse().map_err(|_| anyhow!("bad month"))?;
    let day: u32 = s[6..8].parse().map_err(|_| anyhow!("bad day"))?;
    let hour: u32 = s[8..10].parse().map_err(|_| anyhow!("bad hour"))?;
    let min: u32 = s[10..12].parse().map_err(|_| anyhow!("bad min"))?;
    let sec: u32 = s[12..14].parse().map_err(|_| anyhow!("bad sec"))?;

    let days = days_from_civil(year, month, day)?;
    let seconds = days
        .checked_mul(86400)
        .and_then(|d| d.checked_add((hour as i64) * 3600))
        .and_then(|d| d.checked_add((min as i64) * 60))
        .and_then(|d| d.checked_add(sec as i64))
        .ok_or_else(|| anyhow!("overflow computing seconds"))?;
    Ok(seconds)
}

/// 从 idx 文件名中提取起始时间（格式：{id}_YYYYMMDDhhmmss.mp4.idx）并返回 unix 秒
pub(crate) fn extract_start_time_from_idx_filename(path: &Path, id: &str) -> Result<i64> {
    let name = path
        .file_name()
        .and_then(|s| s.to_str())
        .ok_or_else(|| anyhow!("invalid filename"))?;
    let prefix = format!("{}_", id);
    let suffix = ".mp4.idx";
    if !name.starts_with(&prefix) || !name.ends_with(suffix) {
        return Err(anyhow!(
            "filename does not match expected pattern for id '{}'",
            id
        ));
    }
    let time_part = &name[prefix.len()..name.len() - suffix.len()];
    // parse_filename_time_to_unix already returns Result<i64>, propagate any error
    parse_filename_time_to_unix(time_part)
}

/// helper: SystemTime -> millis since epoch as u64
pub fn system_time_to_millis(t: SystemTime) -> Result<u64> {
    t.duration_since(UNIX_EPOCH)
        .map(|d| d.as_millis() as u64)
        .map_err(|e| anyhow!("system time before UNIX_EPOCH: {}", e))
}

/// helper: format SystemTime into "YYYYMMDDhhmmss" (seconds precision)
pub fn system_time_to_yyyymmddhhmmss(t: SystemTime) -> Result<String> {
    let secs = t
        .duration_since(UNIX_EPOCH)
        .map(|d| d.as_secs() as i64)
        .map_err(|e| anyhow!("system time before UNIX_EPOCH: {}", e))?;
    unix_secs_to_yyyymmddhhmmss(secs)
}
