import os
import json
from platformdirs import user_data_dir

class MetaDataManager:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.init()
        return cls._instance
    def init(self):
        self.socket = None
        # read metadata file from executing directory
        file_path = user_data_dir("ClipTrim")
        os.makedirs(file_path, exist_ok=True)
        print(file_path)
        self.metadata_file = os.path.join(file_path, "metadata.json")
        if os.path.exists(self.metadata_file):
            with open(self.metadata_file, "r") as f:
                self.collections = json.load(f)
        else:
            self.collections = []
        if(collections := next((c for c in self.collections if c.get("name") == "Uncategorized"), None)) is None:
            self.collections.append({"name": "Uncategorized", "id": 0, "clips": []})
            self.save_metadata()
    
    def create_collection(self, name):
        if any(c.get("name") == name for c in self.collections):
            raise ValueError(f"Collection '{name}' already exists.")
        new_id = max((c.get("id", 0) for c in self.collections), default=0) + 1
        self.collections.append({"name": name, "id": new_id, "clips": []})
        self.save_metadata()
    
    def delete_collection(self, name):
        collection = next((c for c in self.collections if c.get("name") == name), None)
        if collection is None:
            raise ValueError(f"Collection '{name}' does not exist.")
        self.collections.remove(collection)
        self.save_metadata()
    
    def add_clip_to_collection(self, collection_name, clip_metadata):
        collection = next((c for c in self.collections if c.get("name") == collection_name), None)
        if collection is None:
            raise ValueError(f"Collection '{collection_name}' does not exist.")
        collection["clips"].append(clip_metadata)
        if not self.socket is None:
            self.socket.emit('collection_updated', collection)
        self.save_metadata()

    def remove_clip_from_collection(self, collection_name, clip_metadata):
        collection = next((c for c in self.collections if c.get("name") == collection_name), None)
        if collection is None:
            raise ValueError(f"Collection '{collection_name}' does not exist.")
        # Remove all clips with the same file name as clip_metadata["file_name"]
        in_list = any(clip.get("filename") == clip_metadata.get("filename") for clip in collection["clips"])
        if not in_list:
            raise ValueError(f"Clip with filename '{clip_metadata.get('filename')}' not found in collection '{collection_name}'.")

        collection["clips"] = [
            clip for clip in collection["clips"]
            if clip.get("filename") != clip_metadata.get("filename")
        ]
        if not self.socket is None:
            self.socket.emit('collection_updated', collection)
        self.save_metadata()
       
    
    def move_clip_to_collection(self, source_collection, target_collection, clip_metadata):
        self.remove_clip_from_collection(source_collection, clip_metadata)
        self.add_clip_to_collection(target_collection, clip_metadata)
        if not self.socket is None:
            self.socket.emit('collection_updated', source_collection)
            self.socket.emit('collection_updated', target_collection)


    def edit_clip_in_collection(self, collection_name, new_clip_metadata):
        collection = next((c for c in self.collections if c.get("name") == collection_name), None)
        if collection is None:
            raise ValueError(f"Collection '{collection_name}' does not exist.")
        # Find the index of the clip with the same file name as old_clip_metadata["file_name"]
        index = next((i for i, clip in enumerate(collection["clips"]) if clip.get("filename") == new_clip_metadata.get("filename")), None)
        if index is None:
            raise ValueError(f"Clip with filename '{new_clip_metadata.get('filename')}' not found in collection '{collection_name}'.")
        
        collection["clips"][index] = new_clip_metadata
        if not self.socket is None:
            self.socket.emit('collection_updated', collection)
        self.save_metadata()
    
    def get_collections(self):
        return list(map(lambda c: {"name": c.get("name"), "id": c.get("id")}, self.collections))
    
    def get_clips_in_collection(self, collection_name):
        collection = next((c for c in self.collections if c.get("name") == collection_name), None)
        if collection is None:
            raise ValueError(f"Collection '{collection_name}' does not exist.")
        return collection["clips"]
    
    def reorder_clips_in_collection(self, collection_name, new_order):
        collection = next((c for c in self.collections if c.get("name") == collection_name), None)
        if collection is None:
            raise ValueError(f"Collection '{collection_name}' does not exist.")
        existing_filenames = {clip.get("filename") for clip in collection["clips"]}
        new_filenames = {clip.get("filename") for clip in new_order}
        
        if not new_filenames.issubset(existing_filenames):
            raise ValueError("New order contains clips that do not exist in the collection.")
        
        collection["clips"] = new_order
        if not self.socket is None:
            self.socket.emit('collection_updated', collection)
        self.save_metadata()
    
    def save_metadata(self):
        with open(self.metadata_file, "w") as f:
            json.dump(self.collections, f, indent=4)
