// Server binary for relay RPCs, HID/audio/video gadget IO, and capture leasing.
#[allow(clippy::useless_attribute)]
#[forbid(unsafe_code)]
use anyhow::Context;
use futures_util::{Stream, StreamExt};
use std::collections::HashMap;
use std::collections::HashSet;
use std::net::SocketAddr;
use std::os::unix::fs::FileTypeExt;
use std::process::Command;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::{backtrace::Backtrace, panic, pin::Pin, sync::Arc, time::Duration};
use tokio::sync::{Mutex, broadcast};
use tokio_stream::wrappers::ReceiverStream;
use tonic::transport::Server;
use tonic::{Request, Response, Status};
use tonic_reflection::server::Builder as ReflBuilder;
use tracing::{debug, error, info, warn};

use lesavka_common::lesavka::{
    AudioPacket, CalibrationRequest, CalibrationState, CapturePowerCommand, CapturePowerState,
    Empty, KeyboardReport, MonitorRequest, MouseReport, OutputDelayProbeReply,
    OutputDelayProbeRequest, PasteReply, PasteRequest, ResetUsbReply, SetCapturePowerRequest,
    UpstreamMediaBundle, UpstreamSyncState, VideoPacket,
    relay_server::{Relay, RelayServer},
};

use lesavka_server::{
    calibration::CalibrationStore,
    camera,
    camera_runtime::CameraRuntime,
    capture_power::CapturePowerManager,
    gadget::UsbGadget,
    handshake::HandshakeSvc,
    paste, runtime_support,
    runtime_support::init_tracing,
    security,
    upstream_media_runtime::{
        PlannedUpstreamPacket, UpstreamClientTiming, UpstreamMediaKind, UpstreamMediaRuntime,
        UpstreamPlanDecision,
    },
    uvc_contract, uvc_runtime, video,
};

/*──────────────── constants ────────────────*/
const PKG_NAME: &str = env!("CARGO_PKG_NAME");

type VideoStream = Pin<Box<dyn Stream<Item = Result<VideoPacket, Status>> + Send>>;
type AudioStream = Pin<Box<dyn Stream<Item = Result<AudioPacket, Status>> + Send>>;
type ResetReply = Result<Response<ResetUsbReply>, Status>;

fn hid_endpoint(index: u8) -> String {
    std::env::var("LESAVKA_HID_DIR")
        .map(|dir| format!("{dir}/hidg{index}"))
        .unwrap_or_else(|_| format!("/dev/hidg{index}"))
}

fn live_keyboard_report_delay() -> Duration {
    std::env::var("LESAVKA_LIVE_KEYBOARD_REPORT_DELAY_MS")
        .ok()
        .and_then(|value| value.parse::<u64>().ok())
        .map(Duration::from_millis)
        .unwrap_or_else(|| Duration::from_millis(8))
}

fn server_bind_addr() -> anyhow::Result<SocketAddr> {
    // LESAVKA_SERVER_BIND_ADDR lets tests and constrained lab benches avoid
    // colliding with another live relay while keeping the field default stable.
    let raw =
        std::env::var("LESAVKA_SERVER_BIND_ADDR").unwrap_or_else(|_| "0.0.0.0:50051".to_string());
    raw.parse::<SocketAddr>()
        .with_context(|| format!("parsing LESAVKA_SERVER_BIND_ADDR={raw:?}"))
}

/*──────────────── Handler ───────────────────*/
struct Handler {
    kb: Arc<Mutex<Option<tokio::fs::File>>>,
    ms: Arc<Mutex<Option<tokio::fs::File>>>,
    gadget: UsbGadget,
    did_cycle: Arc<AtomicBool>,
    camera_rt: Arc<CameraRuntime>,
    upstream_media_rt: Arc<UpstreamMediaRuntime>,
    calibration: Arc<CalibrationStore>,
    capture_power: CapturePowerManager,
    eye_hubs: Arc<Mutex<HashMap<EyeHubKey, Arc<EyeHub>>>>,
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct EyeHubKey {
    source_id: u32,
    requested_width: u32,
    requested_height: u32,
    requested_fps: u32,
}

struct EyeHub {
    tx: broadcast::Sender<VideoPacket>,
    running: Arc<AtomicBool>,
    subscribers: Arc<AtomicUsize>,
    task: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>,
}

include!("main/handler_startup.rs");
include!("main/eye_video.rs");
include!("main/rpc_helpers.rs");

include!("main/usb_recovery_helpers.rs");
include!("main/eye_hub.rs");
include!("main/relay_stream_lifecycle.rs");
include!("main/relay_service.rs");
include!("main/relay_service_coverage.rs");
include!("main/entrypoint.rs");
