import os
import base64
from datetime import datetime
from pymongo.errors import DuplicateKeyError
from pymongo import ReturnDocument
from app.db import database
from app.v1.models.crudmodel import get_model_for_collection

UPLOAD_BASE_DIR = os.path.join(os.getcwd(), "data")

def serialize_item(collection_name: str, item: dict) -> dict:
    auto_field = f"{collection_name}_id"
    item["id"] = item.get(auto_field, str(item["_id"]))
    item.pop("_id", None)
    return item

def get_next_sequence_value(sequence_name: str, db: database.MongoDB) -> int:
    ret = db["counters"].find_one_and_update(
        {"_id": sequence_name},
        {"$inc": {"sequence_value": 1}},
        upsert=True,
        return_document=ReturnDocument.AFTER
    )
    return ret["sequence_value"]

def save_uploaded_file(data_url, account_id, collection_name, item_id, field_name):
    try:
        header, encoded = data_url.split(",", 1)
        mime_type = header.split(";")[0][5:]
        ext_map = {
            "application/pdf": "pdf",
            "image/jpeg": "jpg",
            "image/png": "png",
            "application/msword": "doc",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
            "application/vnd.ms-excel": "xls",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
            "text/csv": "csv",
        }
        ext = ext_map.get(mime_type, "dat")
        dir_path = os.path.join(UPLOAD_BASE_DIR, account_id, collection_name)
        os.makedirs(dir_path, exist_ok=True)
        file_name = f"{item_id}_{field_name}.{ext}"
        file_path = os.path.join(dir_path, file_name)
        file_bytes = base64.b64decode(encoded)

        with open(file_path, "wb") as f:
            f.write(file_bytes)

        return file_path
    except Exception as e:
        raise ValueError(f"Failed to save file for field '{field_name}': {str(e)}")

def process_file_fields(payload, account_id, collection_name, item_id):
    for key, value in payload.items():
        if isinstance(value, str) and value.startswith("data:"):
            payload[key] = save_uploaded_file(value, account_id, collection_name, item_id, key)
        elif isinstance(value, list) and all(isinstance(v, str) and v.startswith("data:") for v in value):
            payload[key] = [
                save_uploaded_file(v, account_id, collection_name, item_id, f"{key}_{i}")
                for i, v in enumerate(value)
            ]
    return payload

def create_item_service(collection_name, payload, db, current_user):
    model_class = get_model_for_collection(collection_name)

    file_fields = {k: v for k, v in payload.items() if isinstance(v, str) and v.startswith("data:") or (isinstance(v, list) and all(isinstance(f, str) and f.startswith("data:") for f in v))}
    normal_fields = {k: v for k, v in payload.items() if k not in file_fields}

    valid_payload = model_class(**normal_fields).dict()

    valid_payload.update({
        "account_id": current_user.get('account_id'),
        "user_id": current_user.get('id'),
        f"{collection_name}_id": get_next_sequence_value(f"{collection_name}_id", db),
        "created_date": datetime.utcnow()
    })

    valid_payload = process_file_fields({**valid_payload, **file_fields}, current_user.get('account_id'), collection_name, valid_payload[f"{collection_name}_id"])

    result = db[collection_name].insert_one(valid_payload)
    return serialize_item(collection_name, db[collection_name].find_one({"_id": result.inserted_id}))

def get_items_service(collection_name, skip, limit, db, current_user):
    filter_ = {} if current_user.get('roles') == 1 else {"account_id": current_user.get('account_id')}
    items = list(db[collection_name].find(filter_).skip(skip).limit(limit))
    total_count = db[collection_name].count_documents(filter_)
    return {"total_count": total_count, "items": [serialize_item(collection_name, i) for i in items]}

def get_item_service(collection_name, item_id, db, current_user):
    auto_field = f"{collection_name}_id"
    query = {auto_field: int(item_id)}
    if current_user.get('roles') != 1:
        query["account_id"] = current_user.get('account_id')
    return serialize_item(collection_name, db[collection_name].find_one(query))

def update_item_service(collection_name, item_id, payload, db, current_user):
    model_class = get_model_for_collection(collection_name)

    file_fields = {k: v for k, v in payload.items() if isinstance(v, str) and v.startswith("data:") or (isinstance(v, list) and all(isinstance(f, str) and f.startswith("data:") for f in v))}
    normal_fields = {k: v for k, v in payload.items() if k not in file_fields}

    valid_payload = model_class(**normal_fields).dict()
    valid_payload.update({
        "account_id": current_user.get('account_id'),
        "user_id": current_user.get('id'),
        "updated_date": datetime.utcnow()
    })

    valid_payload = process_file_fields({**valid_payload, **file_fields}, current_user.get('account_id'), collection_name, item_id)

    query = {f"{collection_name}_id": int(item_id)}
    if current_user.get('roles') != 1:
        query["account_id"] = current_user.get('account_id')

    result = db[collection_name].update_one(query, {"$set": valid_payload})
    if result.matched_count == 0:
        raise ValueError("Item not found")

    return serialize_item(collection_name, db[collection_name].find_one(query))

def delete_item_service(collection_name, item_id, db, current_user):
    query = {f"{collection_name}_id": int(item_id)}
    if current_user.get('roles') != 1:
        query["account_id"] = current_user.get('account_id')

    item = db[collection_name].find_one(query)
    if not item:
        raise ValueError("Item not found")

    db[collection_name].delete_one(query)
    return serialize_item(collection_name, item)