use crate::domain::guest_share::GuestShare;
use crate::domain::record_id::RecordId;
use crate::ports::storage::StoragePort;
use crate::ports::repository::GuestShareRepositoryPort;
use crate::ports::id_generator::IdGeneratorPort;
use crate::error::{Result, JottamError};
use chrono::{Utc, Duration};

pub struct GuestShareUseCase<S: StoragePort, R: GuestShareRepositoryPort, G: IdGeneratorPort> {
    storage: S,
    share_repo: R,
    id_gen: G,
}

impl<S: StoragePort, R: GuestShareRepositoryPort, G: IdGeneratorPort> GuestShareUseCase<S, R, G> {
    pub fn new(storage: S, share_repo: R, id_gen: G) -> Self {
        Self { storage, share_repo, id_gen }
    }

    pub async fn create_share(
        &self,
        file_data: &[u8],
        name: String,
        mime_type: String,
        size: u64,
        is_encrypted: bool,
        encrypted_metadata: Option<Vec<u8>>,
        salt: Option<Vec<u8>>,
    ) -> Result<GuestShare> {
        let share_id = self.id_gen.generate();
        let storage_path = self.storage.upload_file(file_data, None).await?;

        let now = Utc::now();
        let expires_at = now + Duration::hours(24);

        let share = GuestShare {
            id: share_id,
            storage_path,
            name,
            mime_type,
            size,
            is_encrypted,
            encrypted_metadata,
            salt,
            created_at: now,
            expires_at,
            is_multipart: false,
            chunk_size: None,
        };

        self.share_repo.save(share.clone()).await?;
        Ok(share)
    }

    pub async fn init_multipart_upload(&self, num_parts: i32) -> Result<(String, String, Vec<(i32, String)>, RecordId)> {
        let share_id = self.id_gen.generate();
        let storage_ref = format!("guests/{}", uuid::Uuid::new_v4());
        
        let upload_id = self.storage.create_multipart_upload(&storage_ref, None).await?;
        
        let mut part_urls = Vec::with_capacity(num_parts as usize);
        for part_num in 1..=num_parts {
            let url = self.storage.generate_presigned_part_url(&storage_ref, &upload_id, part_num).await?;
            part_urls.push((part_num, url));
        }
        
        Ok((upload_id, storage_ref, part_urls, share_id))
    }

    pub async fn complete_multipart_upload(
        &self,
        share_id: RecordId,
        upload_id: &str,
        storage_ref: &str,
        parts: Vec<(i32, String)>,
        name: String,
        mime_type: String,
        size: u64,
        is_encrypted: bool,
        encrypted_metadata: Option<Vec<u8>>,
        salt: Option<Vec<u8>>,
    ) -> Result<GuestShare> {
        self.storage.complete_multipart_upload(storage_ref, upload_id, parts).await?;
        
        let now = Utc::now();
        let expires_at = now + Duration::hours(24);

        let share = GuestShare {
            id: share_id,
            storage_path: storage_ref.to_string(),
            name,
            mime_type,
            size,
            is_encrypted,
            encrypted_metadata,
            salt,
            created_at: now,
            expires_at,
            is_multipart: true,
            chunk_size: Some(5 * 1024 * 1024), // 5MB standardized chunk size
        };

        self.share_repo.save(share.clone()).await?;
        Ok(share)
    }

    pub async fn get_share(&self, id: &RecordId) -> Result<GuestShare> {
        let share = self.share_repo.find_by_id(id).await?
            .ok_or_else(|| JottamError::NotFound("Guest share not found".to_string()))?;

        if share.expires_at < Utc::now() {
            // Optionally delete it now
            let _ = self.share_repo.delete(id).await;
            return Err(JottamError::NotFound("Guest share has expired".to_string()));
        }

        Ok(share)
    }

    pub async fn get_download_url(&self, share: &GuestShare) -> Result<String> {
        self.storage.generate_presigned_url(&share.storage_path, std::time::Duration::from_secs(3600)).await
    }
}
