use crate::GitError;
use crate::hook::sync::HookMetaDataSync;
use db::database::AppTransaction;
use models::repos::repo_lfs_object;
use sea_orm::*;
use std::collections::HashSet;

impl HookMetaDataSync {
    pub async fn sync_lfs_objects(&self, txn: &AppTransaction) -> Result<(), GitError> {
        let repo_id = self.repo.id;

        let existing: Vec<repo_lfs_object::Model> = repo_lfs_object::Entity::find()
            .filter(repo_lfs_object::Column::Repo.eq(repo_id))
            .all(txn)
            .await
            .map_err(|e| GitError::IoError(format!("failed to query lfs objects: {}", e)))?;

        let mut existing_oids: HashSet<String> = existing.into_iter().map(|o| o.oid).collect();

        let local_oids = self.domain.lfs_object_list()?;
        let now = chrono::Utc::now();

        let mut new_objects = Vec::new();

        for oid in local_oids {
            let oid_str = oid.as_str().to_string();

            if existing_oids.contains(&oid_str) {
                existing_oids.remove(&oid_str);
                continue;
            }

            let path = match self.domain.lfs_object_path(&oid) {
                Ok(p) => p,
                Err(e) => {
                    tracing::warn!("invalid LFS OID in local objects directory error={}", e);
                    continue;
                }
            };
            let size = if let Ok(meta) = std::fs::metadata(&path) {
                meta.len() as i64
            } else {
                continue;
            };

            let storage_path = path.to_string_lossy().to_string();

            new_objects.push(repo_lfs_object::ActiveModel {
                repo: Set(repo_id),
                oid: Set(oid_str),
                size: Set(size),
                storage_path: Set(storage_path),
                uploaded_by: Set(None),
                uploaded_at: Set(now),
                ..Default::default()
            });
        }

        if !new_objects.is_empty() {
            // Insert in batches
            for chunk in new_objects.chunks(100) {
                repo_lfs_object::Entity::insert_many(chunk.to_vec())
                    .exec(txn)
                    .await
                    .map_err(|e| {
                        GitError::IoError(format!("failed to insert lfs objects: {}", e))
                    })?;
            }
        }

        // Remove objects that no longer exist on disk
        if !existing_oids.is_empty() {
            repo_lfs_object::Entity::delete_many()
                .filter(repo_lfs_object::Column::Repo.eq(repo_id))
                .filter(repo_lfs_object::Column::Oid.is_in(existing_oids))
                .exec(txn)
                .await
                .map_err(|e| {
                    GitError::IoError(format!("failed to delete stale lfs objects: {}", e))
                })?;
        }

        Ok(())
    }
}
