use core::{any::Any, fmt, ops::Deref};

use alloc::{
    boxed::Box,
    string::String,
    sync::{Arc, Weak},
    vec::Vec,
};
use async_trait::async_trait;
use libk::{
    error::Error,
    fs::sysfs::{
        self,
        attribute::{StringAttribute, StringAttributeOps},
        object::KObject,
    },
};
use libk_util::OneTimeInit;

use crate::{
    descriptor::{UsbDeviceDescriptor, UsbHubDescriptorHeader},
    error::UsbError,
    host::UsbHostController,
    info::{PortString, UsbConfigurationInfo, UsbEndpointInfo, UsbEndpointType, UsbInterfaceInfo},
    pipe::{
        control::{ConfigurationDescriptorEntry, UsbControlPipeAccess},
        normal::{
            UsbBulkInPipeAccess, UsbBulkOutPipeAccess, UsbInterruptInPipeAccess, UsbNormalPipeIn,
            UsbNormalPipeOut,
        },
    },
};

// High-level structures for info provided through descriptors
type UsbDeviceKObject = KObject<Weak<UsbDeviceAccess>>;

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct UsbBusAddress {
    pub bus: u16,
    pub device: u8,
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct UsbInterfaceAddress {
    pub bus: UsbBusAddress,
    pub interface: u8,
}

pub struct UsbDeviceAccess {
    pub device: Arc<dyn UsbDevice>,
    pub device_descriptor: UsbDeviceDescriptor,
    pub product_str: Option<String>,
    pub vendor_str: Option<String>,
    pub configuration0_info: UsbConfigurationInfo,

    kobject: OneTimeInit<Arc<UsbDeviceKObject>>,
}

// USB device, limited in scope by one interface
#[derive(Clone)]
pub struct UsbInterfaceAccess {
    device: Arc<UsbDeviceAccess>,
    interface_index: u8,
    endpoint_start_index: u8,
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum UsbSpeed {
    Low,
    Full,
    High,
    Super,
}

#[async_trait]
pub trait UsbDeviceDetachHandler: Send + Sync {
    async fn handle_device_detach(&self);
}

#[async_trait]
#[allow(unused)]
pub trait UsbDevice: Send + Sync {
    // Endpoint "0"
    fn control_pipe(&self) -> &UsbControlPipeAccess;

    async fn open_normal_in_pipe(
        &self,
        number: u8,
        max_packet_size: u16,
        ty: UsbEndpointType,
    ) -> Result<Box<dyn UsbNormalPipeIn>, UsbError>;

    async fn open_normal_out_pipe(
        &self,
        number: u8,
        max_packet_size: u16,
        ty: UsbEndpointType,
    ) -> Result<Box<dyn UsbNormalPipeOut>, UsbError>;

    async fn configure_hub(&self, hub_descriptor: &UsbHubDescriptorHeader) -> Result<(), UsbError>;

    // fn port_number(&self) -> u8;
    fn port_string(&self) -> &PortString;
    fn bus_address(&self) -> UsbBusAddress;
    fn speed(&self) -> UsbSpeed;
    fn host_controller(&self) -> Arc<dyn UsbHostController>;
    fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;

    fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>);
    async fn handle_detach(&self);

    fn debug(&self) {}
}

impl UsbDeviceAccess {
    fn usb_kobject() -> Option<&'static Arc<KObject<()>>> {
        static USB_KOBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();

        USB_KOBJECT.or_init_with_opt(|| {
            let bus_kobject = sysfs::bus()?;
            let usb_kobject = KObject::new(());
            bus_kobject.add_object("usb", usb_kobject.clone()).ok()?;
            Some(usb_kobject)
        })
    }

    fn setup_kobject(self: &Arc<Self>) -> Result<(), Error> {
        struct Id;
        struct DeviceClass;
        struct VendorString;
        struct ProductString;

        impl StringAttributeOps for Id {
            type Data = Weak<UsbDeviceAccess>;
            const NAME: &'static str = "id";

            fn read(state: &Self::Data) -> Result<String, Error> {
                let state = state.upgrade().ok_or(Error::InvalidOperation)?;
                let id_product = state.device_descriptor.id_product;
                let id_vendor = state.device_descriptor.id_vendor;
                Ok(alloc::format!("{id_vendor:04x}:{id_product:04x}",))
            }
        }

        impl StringAttributeOps for DeviceClass {
            type Data = Weak<UsbDeviceAccess>;
            const NAME: &'static str = "device_class";

            fn read(state: &Self::Data) -> Result<String, Error> {
                let state = state.upgrade().ok_or(Error::InvalidOperation)?;
                Ok(alloc::format!(
                    "{:02x}:{:02x}:{:02x}",
                    state.device_descriptor.device_class,
                    state.device_descriptor.device_subclass,
                    state.device_descriptor.device_protocol
                ))
            }
        }

        impl StringAttributeOps for VendorString {
            type Data = Weak<UsbDeviceAccess>;
            const NAME: &'static str = "vendor_str";

            fn read(state: &Self::Data) -> Result<String, Error> {
                let state = state.upgrade().ok_or(Error::InvalidOperation)?;
                if let Some(vendor_str) = state.vendor_str.as_ref() {
                    Ok(vendor_str.clone())
                } else {
                    Ok(String::new())
                }
            }
        }

        impl StringAttributeOps for ProductString {
            type Data = Weak<UsbDeviceAccess>;
            const NAME: &'static str = "product_str";

            fn read(state: &Self::Data) -> Result<String, Error> {
                let state = state.upgrade().ok_or(Error::InvalidOperation)?;
                if let Some(product_str) = state.product_str.as_ref() {
                    Ok(product_str.clone())
                } else {
                    Ok(String::new())
                }
            }
        }

        let usb_kobject = Self::usb_kobject().ok_or(Error::DoesNotExist)?;
        let dev_kobject = KObject::new(Arc::downgrade(self));
        dev_kobject.add_attribute(StringAttribute::from(Id)).ok();
        dev_kobject
            .add_attribute(StringAttribute::from(DeviceClass))
            .ok();
        dev_kobject
            .add_attribute(StringAttribute::from(ProductString))
            .ok();
        dev_kobject
            .add_attribute(StringAttribute::from(VendorString))
            .ok();
        let name = alloc::format!("{}", self.device.bus_address());
        usb_kobject.add_object(name, dev_kobject.clone())?;
        self.kobject.init(dev_kobject);
        Ok(())
    }

    /// Expected device state:
    ///
    /// * Link-layer stuff has been reset and established properly by the HCD
    /// * Device is not yet configured
    /// * Control pipe for the device has been properly set up
    /// * Device has been assigned a bus address
    pub async fn setup(raw: Arc<dyn UsbDevice>) -> Result<Arc<Self>, UsbError> {
        let control_pipe = raw.control_pipe();
        let device_descriptor = control_pipe.query_device_descriptor().await?;
        if device_descriptor.num_configurations < 1 {
            return Err(UsbError::InvalidDescriptorField);
        }
        let config_descriptor = control_pipe.query_configuration_descriptor(0).await?;

        // Use configuration 0
        control_pipe
            .set_configuration(config_descriptor.configuration().config_val as _)
            .await?;

        let vendor_str = control_pipe
            .query_string(device_descriptor.manufacturer_str)
            .await
            .inspect_err(|e| {
                log::warn!(
                    "{}: manufacturer string query error: {:?}",
                    raw.bus_address(),
                    e
                )
            })
            .ok();
        let product_str = control_pipe
            .query_string(device_descriptor.product_str)
            .await
            .inspect_err(|e| {
                log::warn!("{}: product string query error: {:?}", raw.bus_address(), e)
            })
            .ok();

        // Extract configuration 0 information
        // let query = control_pipe.query_configuration_descriptor(index).await?;

        let configuration_name = control_pipe
            .query_string(config_descriptor.configuration().config_str)
            .await
            .ok();

        let mut endpoints = Vec::new();
        let mut interfaces = Vec::new();

        for desc in config_descriptor.descriptors() {
            match desc {
                ConfigurationDescriptorEntry::Interface(iface) => {
                    let name = control_pipe.query_string(iface.interface_str).await.ok();
                    interfaces.push(UsbInterfaceInfo {
                        name,
                        number: iface.interface_number,
                        num_endpoints: iface.num_endpoints,

                        interface_class: iface.interface_class,
                        interface_subclass: iface.interface_subclass,
                        interface_protocol: iface.interface_protocol,
                    });
                }
                ConfigurationDescriptorEntry::Endpoint(ep) => {
                    endpoints.push(UsbEndpointInfo {
                        number: ep.number(),
                        direction: ep.direction(),
                        max_packet_size: ep.max_packet_size as _,
                        ty: ep.transfer_type(),
                    });
                }
                _ => (),
            }
        }

        interfaces.sort_by_key(|r| r.number);
        endpoints.sort_by_key(|r| r.number);

        let configuration0_info = UsbConfigurationInfo {
            name: configuration_name,
            config_value: config_descriptor.configuration().config_val,
            interfaces,
            endpoints,
        };

        let device = Arc::new(Self {
            device: raw,
            device_descriptor,
            product_str,
            vendor_str,
            configuration0_info,
            kobject: OneTimeInit::new(),
        });

        if let Err(error) = device.setup_kobject() {
            log::error!(
                "{} kobject setup error: {:?}",
                device.device.bus_address(),
                error
            );
        }

        Ok(device)
    }

    pub fn interface(self: &Arc<Self>, interface_index: u8) -> UsbInterfaceAccess {
        let endpoint_start_index = self
            .configuration0_info
            .interfaces
            .iter()
            .take(interface_index as usize)
            .map(|r| r.num_endpoints)
            .sum();

        UsbInterfaceAccess {
            device: self.clone(),
            interface_index,
            endpoint_start_index,
        }
    }

    pub async fn open_interrupt_in_pipe(
        &self,
        number: u8,
        max_packet_size: u16,
    ) -> Result<UsbInterruptInPipeAccess, UsbError> {
        let pipe = self
            .device
            .open_normal_in_pipe(number, max_packet_size, UsbEndpointType::Interrupt)
            .await?;
        Ok(UsbInterruptInPipeAccess(pipe))
    }

    pub async fn open_bulk_in_pipe(
        &self,
        number: u8,
        max_packet_size: u16,
    ) -> Result<UsbBulkInPipeAccess, UsbError> {
        let pipe = self
            .device
            .open_normal_in_pipe(number, max_packet_size, UsbEndpointType::Bulk)
            .await?;
        Ok(UsbBulkInPipeAccess(pipe))
    }

    pub async fn open_bulk_out_pipe(
        &self,
        number: u8,
        max_packet_size: u16,
    ) -> Result<UsbBulkOutPipeAccess, UsbError> {
        let pipe = self
            .device
            .open_normal_out_pipe(number, max_packet_size, UsbEndpointType::Bulk)
            .await?;
        Ok(UsbBulkOutPipeAccess(pipe))
    }

    pub fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>) {
        self.device.set_detach_handler(handler);
    }
}

impl Deref for UsbDeviceAccess {
    type Target = dyn UsbDevice;

    fn deref(&self) -> &Self::Target {
        &*self.device
    }
}

impl fmt::Display for UsbBusAddress {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", self.bus, self.device)
    }
}

impl fmt::Display for UsbInterfaceAddress {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}.{}", self.bus, self.interface)
    }
}

impl UsbInterfaceAccess {
    fn interface_info(&self) -> &UsbInterfaceInfo {
        &self.device.configuration0_info.interfaces[self.interface_index as usize]
    }

    pub fn number(&self) -> u8 {
        self.interface_index
    }

    pub fn endpoints(&self) -> &[UsbEndpointInfo] {
        let info = self.interface_info();
        let start = self.endpoint_start_index as usize;
        let end = start + info.num_endpoints as usize;
        &self.device.configuration0_info.endpoints[start..end]
    }

    pub fn device(&self) -> &Arc<UsbDeviceAccess> {
        &self.device
    }

    pub fn address(&self) -> UsbInterfaceAddress {
        UsbInterfaceAddress {
            bus: self.device.bus_address(),
            interface: self.interface_index,
        }
    }

    pub fn class(&self) -> (u8, u8, u8) {
        let i = self.interface_info();
        (
            i.interface_class,
            i.interface_subclass,
            i.interface_protocol,
        )
    }
}
