from fastapi import FastAPI, HTTPException, Depends, File, UploadFile, Form
from fastapi.responses import FileResponse, HTMLResponse
from pydantic import BaseModel, EmailStr
from datetime import datetime
from typing import Literal, Optional
import sqlite3
import bcrypt
import string
import secrets
import os

from database import DB_NAME, calculate_tier_pricing_and_expiration
from database import MASTER_ADMIN_CODES
from database import init_database_system

app = FastAPI(title="Project Electricol API")

try:
    init_database_system()
except Exception as e:
    print(f"❌ Fatal Database Boot Error: {str(e)}")

@app.on_event("startup")
def configure_system_startup():
    init_database_system()

# --- GLOBAL FINANCES & COMS MATRIX ---
DEPOSIT_CONFIG = {
    "binance": {"status": "off"},
    "local": {"status": "off"},
    "crypto": {
        "status": "on",
        "crypto_name": "USDT",
        "network": "TRC-20 (Tron Network)",
        "address": "TYqAn7Yn7XmR8wKzUvA84NqS92mExP41bA"
    },
    "comms": {
        "whatsapp_phone": "254700000000",
        "telegram_bot": "electricol_escrow_bot",
        "gmail_address": "escrow@electricol.com"
    },
    "agents": [
        {"id": 0, "name": "Kimani Capital Node", "rate": "131.45", "status": "on", "whatsapp": "254700000000", "telegram": "electricol_escrow_bot", "gmail": "escrow@electricol.com"},
        {"id": 1, "name": "Onyango Liquidity Desk", "rate": "130.90", "status": "on", "whatsapp": "254700000000", "telegram": "electricol_escrow_bot", "gmail": "escrow@electricol.com"},
        {"id": 2, "name": "Wanjiku Escrow Switch", "rate": "131.80", "status": "on", "whatsapp": "254700000000", "telegram": "electricol_escrow_bot", "gmail": "escrow@electricol.com"},
        {"id": 3, "name": "Mombasa Remit Hub", "rate": "132.10", "status": "on", "whatsapp": "254700000000", "telegram": "electricol_escrow_bot", "gmail": "escrow@electricol.com"},
        {"id": 4, "name": "Juja Core Currency", "rate": "131.00", "status": "on", "whatsapp": "254700000000", "telegram": "electricol_escrow_bot", "gmail": "escrow@electricol.com"},
        {"id": 5, "name": "Nairobi Trust Remittance", "rate": "130.85", "status": "on", "whatsapp": "254700000000", "telegram": "electricol_escrow_bot", "gmail": "escrow@electricol.com"},
        {"id": 6, "name": "Rift Value Liquidity", "rate": "131.70", "status": "on", "whatsapp": "254700000000", "telegram": "electricol_escrow_bot", "gmail": "escrow@electricol.com"},
        {"id": 7, "name": "Cheruiyot Forex Exchange", "rate": "131.25", "status": "on", "whatsapp": "254700000000", "telegram": "electricol_escrow_bot", "gmail": "escrow@electricol.com"},
        {"id": 8, "name": "Mutua Handshake Wire", "rate": "131.95", "status": "on", "whatsapp": "254700000000", "telegram": "electricol_escrow_bot", "gmail": "escrow@electricol.com"},
        {"id": 9, "name": "Victoria Ledger Broker", "rate": "131.50", "status": "on", "whatsapp": "254700000000", "telegram": "electricol_escrow_bot", "gmail": "escrow@electricol.com"}
    ]
}

@app.get("/deposit/config")
def get_deposit_system_config():
    """Fetches live toggle and communication strings for frontend compilation mapping."""
    return DEPOSIT_CONFIG

@app.post("/admin/deposit/toggle")
def administrative_deposit_toggle(admin_id: int, method: Literal["binance", "crypto", "local"], status: Literal["on", "off"]):
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT role FROM users WHERE id = ?", (admin_id,))
    operator = cursor.fetchone()
    if not operator or operator["role"] != "admin":
        conn.close()
        raise HTTPException(status_code=403, detail="Access Denied. Administrative clearance required.")
    conn.close()
    
    if method in DEPOSIT_CONFIG:
        DEPOSIT_CONFIG[method]["status"] = status
        return {"status": "Success", "message": f"Gateway channel [{method}] set to [{status}]."}
    raise HTTPException(status_code=400, detail="Invalid target gateway selector.")

# --- DYNAMIC AGENT DATA VALIDATION SCHEMAS ---
class AgentUpdateItem(BaseModel):
    id: int
    name: str
    rate: str

class MasterConfigSavePack(BaseModel):
    admin_id: int
    crypto_name: str
    network: str
    address: str
    whatsapp_phone: str
    telegram_bot: str
    gmail_address: str
    agents: list[AgentUpdateItem]

class SingleAgentUpdatePack(BaseModel):
    admin_id: int
    agent_id: int
    name: str
    rate: str
    status: Literal["on", "off"]
    whatsapp: str
    telegram: str
    gmail: str

# --- DYNAMIC COLLECTION MODEL SCHEMAS ---
class CollectionCreatePack(BaseModel):
    admin_id: int
    name: str

class CollectionUpdatePack(BaseModel):
    admin_id: int
    collection_id: int
    new_name: str

@app.post("/admin/deposit/save-master")
def save_master_system_configurations(pack: MasterConfigSavePack):
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT role FROM users WHERE id = ?", (pack.admin_id,))
    operator = cursor.fetchone()
    if not operator or operator["role"] != "admin":
        conn.close()
        raise HTTPException(status_code=403, detail="Access Denied. Administrative clearance required.")
    conn.close()
    
    DEPOSIT_CONFIG["crypto"]["crypto_name"] = pack.crypto_name
    DEPOSIT_CONFIG["crypto"]["network"] = pack.network
    DEPOSIT_CONFIG["crypto"]["address"] = pack.address
    
    DEPOSIT_CONFIG["comms"]["whatsapp_phone"] = pack.whatsapp_phone
    DEPOSIT_CONFIG["comms"]["telegram_bot"] = pack.telegram_bot
    DEPOSIT_CONFIG["comms"]["gmail_address"] = pack.gmail_address
    
    for item in pack.agents:
        for agent in DEPOSIT_CONFIG["agents"]:
            if agent["id"] == item.id:
                agent["name"] = item.name
                agent["rate"] = item.rate
                break
                
    return {"status": "Success", "message": "Global environment settings successfully updated."}

@app.post("/admin/deposit/agent-update")
def update_individual_agent_credentials(pack: SingleAgentUpdatePack):
    """Calibrates name handles, rates, node visibility, and distinct communication streams for a target slot."""
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT role FROM users WHERE id = ?", (pack.admin_id,))
    operator = cursor.fetchone()
    if not operator or operator["role"] != "admin":
        conn.close()
        raise HTTPException(status_code=403, detail="Access Denied. Administrative clearance required.")
    conn.close()
    
    for agent in DEPOSIT_CONFIG["agents"]:
        if agent["id"] == pack.agent_id:
            agent["name"] = pack.name
            agent["rate"] = pack.rate
            agent["status"] = pack.status
            agent["whatsapp"] = pack.whatsapp
            agent["telegram"] = pack.telegram
            agent["gmail"] = pack.gmail
            return {
                "status": "Success", 
                "message": f"Agent Node '{pack.name}' (ID: {pack.agent_id}) calibrated cleanly with specific target links.",
                "agent": agent
            }
            
    raise HTTPException(status_code=404, detail="Target agent broker index node not found.")

# --- COLLECTION MANAGEMENT SYSTEMS ROUTES ---
@app.get("/admin/collections")
def list_system_collections():
    """Fetches all dynamic collection folders configured on the backend partition."""
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT id, name FROM collections")
    rows = cursor.fetchall()
    conn.close()
    return [{"id": r["id"], "name": r["name"]} for r in rows]

@app.post("/admin/collections/create")
def administrative_create_collection(pack: CollectionCreatePack):
    """Generates a secure custom directory categorization block for catalog inventory items."""
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT role FROM users WHERE id = ?", (pack.admin_id,))
    operator = cursor.fetchone()
    if not operator or operator["role"] != "admin":
        conn.close()
        raise HTTPException(status_code=403, detail="Access Denied. Root operator authorization required.")
    
    try:
        cursor.execute("INSERT INTO collections (name) VALUES (?)", (pack.name.strip(),))
        conn.commit()
    except sqlite3.IntegrityError:
        conn.close()
        raise HTTPException(status_code=400, detail="A catalog collection matching this name handle already exists.")
    
    conn.close()
    return {"status": "Success", "message": f"Collection Folder '{pack.name}' deployed successfully to the database schema."}

@app.post("/admin/collections/update")
def administrative_rename_collection(pack: CollectionUpdatePack):
    """Updates an active folder index handle title and translates all associated file links cleanly."""
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT role FROM users WHERE id = ?", (pack.admin_id,))
    operator = cursor.fetchone()
    if not operator or operator["role"] != "admin":
        conn.close()
        raise HTTPException(status_code=403, detail="Access Denied. Root operator authorization required.")
        
    cursor.execute("SELECT name FROM collections WHERE id = ?", (pack.collection_id,))
    old_meta = cursor.fetchone()
    if not old_meta:
        conn.close()
        raise HTTPException(status_code=404, detail="Target collection identity reference string not found.")
    
    try:
        cursor.execute("UPDATE collections SET name = ? WHERE id = ?", (pack.new_name.strip(), pack.collection_id))
        cursor.execute("UPDATE files SET collection = ? WHERE collection = ?", (pack.new_name.strip(), old_meta["name"]))
        conn.commit()
    except Exception as e:
        conn.close()
        raise HTTPException(status_code=500, detail=f"Core database row shift exception error: {str(e)}")
        
    conn.close()
    return {"status": "Success", "message": f"Dynamic partition folder successfully re-branded to [{pack.new_name}]."}

# --- DATA MODELS ---
class FileCreate(BaseModel):
    title: str
    description: str
    base_price: float = 2.00  
    secure_file_path: str
    collection: str = "General Patches"

class FilePurchase(BaseModel):
    user_id: int
    file_id: int
    package_type: Literal["1_time", "24_hours", "1_month", "1_year", "5_years", "forever"]

class UserRegister(BaseModel):
    username: str
    email: EmailStr
    admin_code: Optional[str] = None

class WalletDeposit(BaseModel):
    user_id: int
    amount: float

# --- HELPER FUNCTIONS ---
def get_db_connection():
    conn = sqlite3.connect(DB_NAME)
    conn.row_factory = sqlite3.Row  
    return conn

def hash_password(password: str) -> str:
    password_bytes = password.encode('utf-8')
    salt = bcrypt.gensalt()
    hashed = bcrypt.hashpw(password_bytes, salt)
    return hashed.decode('utf-8')

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return bcrypt.checkpw(plain_password.encode('utf-8'), hashed_password.encode('utf-8'))

# --- API ROUTES ---

@app.get("/", response_class=HTMLResponse)
def serve_control_panel():
    html_file_path = os.path.join("templates", "index.html")
    if not os.path.exists(html_file_path):
        raise HTTPException(status_code=404, detail="Frontend template index.html missing from disk.")
    with open(html_file_path, "r", encoding="utf-8") as file:
        content = file.read()
    return HTMLResponse(content=content)

@app.get("/login")
def login_user_profile(username: str, password: str):
    conn = get_db_connection()
    cursor = conn.cursor()
    
    try:
        cursor.execute(
            "SELECT id, wallet_balance, role, password_hash FROM users WHERE username = ? COLLATE NOCASE", 
            (username,)
        )
        user = cursor.fetchone()
    except Exception as e:
        conn.close()
        raise HTTPException(status_code=500, detail=f"Database execution error: {str(e)}")
    
    conn.close()
    
    if not user:
        raise HTTPException(status_code=404, detail="Access Denied. Account username signature not found on network.")
        
    database_hash = user["password_hash"]
    if not bcrypt.checkpw(password.encode('utf-8'), database_hash.encode('utf-8')):
        raise HTTPException(status_code=401, detail="Access Denied. Invalid cryptographic password key sequence.")
        
    return {
        "id": user["id"],
        "wallet_balance": float(user["wallet_balance"]),
        "role": user["role"]
    }

@app.post("/register")
def register_user(user: UserRegister):
    conn = get_db_connection()
    cursor = conn.cursor()
    
    cursor.execute(
        "SELECT id FROM users WHERE username = ? COLLATE NOCASE OR email = ?", 
        (user.username, user.email)
    )
    if cursor.fetchone():
        conn.close()
        raise HTTPException(status_code=400, detail="Username or Email already registered.")
    
    alphabet = string.ascii_letters + string.digits + "!@#$%^&*()_+-="
    generated_password = ''.join(secrets.choice(alphabet) for _ in range(16))
    hashed_pwd = hash_password(generated_password)
    assigned_role = "user"
    
    if user.admin_code is not None:
        if not user.username.startswith("_"):
            conn.close()
            raise HTTPException(status_code=400, detail="Administrative naming syntax error. Admin usernames must initialize with an underscore '_'")
            
        if user.admin_code in MASTER_ADMIN_CODES:
            assigned_role = "admin"
            print(f"⚠️ Security Node Alert: Account '{user.username}' elevated to ADMIN via secret code verification.")
        else:
            conn.close()
            raise HTTPException(status_code=403, detail="Access Denied. Invalid administrative authorization key string.")
            
    elif user.username.startswith("_"):
        conn.close()
        raise HTTPException(status_code=400, detail="Reserved Naming Layer. Usernames starting with '_' are strictly guarded for core administrative staff.")

    try:
        cursor.execute(
            "INSERT INTO users (username, email, password_hash, wallet_balance, role) VALUES (?, ?, ?, ?, ?)",
            (user.username, user.email, hashed_pwd, 0.00, assigned_role)
        )
        conn.commit()
        user_id = cursor.lastrowid
    except Exception as e:
        conn.close()
        raise HTTPException(status_code=500, detail=f"Database error during generation sequence: {str(e)}")
        
    conn.close()
    return {
        "message": f"Account profile constructed successfully as {assigned_role}!", 
        "user_id": user_id,
        "username": user.username,
        "generated_password": generated_password
    }

@app.get("/admin/users")
def get_all_users_matrix(admin_id: int):
    conn = get_db_connection()
    cursor = conn.cursor()
    
    cursor.execute("SELECT role FROM users WHERE id = ?", (admin_id,))
    operator = cursor.fetchone()
    if not operator or operator["role"] != "admin":
        conn.close()
        raise HTTPException(status_code=403, detail="Access Denied. Root clearance required.")
        
    cursor.execute("SELECT id, username, email, wallet_balance, role FROM users")
    users_rows = cursor.fetchall()
    conn.close()
    
    return [
        {
            "id": row["id"],
            "username": row["username"],
            "email": row["email"],
            "wallet_balance": float(row["wallet_balance"]),
            "role": row["role"]
        } for row in users_rows
    ]

@app.post("/admin/recharge-wallet")
def manual_wallet_recharge(admin_id: int, target_user_id: int, credit_amount: float):
    if credit_amount <= 0:
        raise HTTPException(status_code=400, detail="Recharge injection amount must be greater than zero.")
        
    conn = get_db_connection()
    cursor = conn.cursor()
    
    cursor.execute("SELECT role FROM users WHERE id = ?", (admin_id,))
    operator = cursor.fetchone()
    if not operator or operator["role"] != "admin":
        conn.close()
        raise HTTPException(status_code=403, detail="Access Denied. Root clearance required.")
        
    cursor.execute("SELECT username, wallet_balance FROM users WHERE id = ?", (target_user_id,))
    target = cursor.fetchone()
    if not target:
        conn.close()
        raise HTTPException(status_code=404, detail="Target user profile structure not found.")
        
    new_balance = float(target["wallet_balance"]) + credit_amount
    cursor.execute("UPDATE users SET wallet_balance = ? WHERE id = ?", (new_balance, target_user_id))
    conn.commit()
    conn.close()
    
    return {
        "status": "Success",
        "message": f"Successfully injected +${credit_amount:.2f} to account '{target['username']}'. New Balance: ${new_balance:.2f}"
    }

@app.post("/admin/regenerate-password")
def administrative_password_regeneration(admin_id: int, target_user_id: int):
    conn = get_db_connection()
    cursor = conn.cursor()
    
    cursor.execute("SELECT role FROM users WHERE id = ?", (admin_id,))
    operator = cursor.fetchone()
    if not operator or operator["role"] != "admin":
        conn.close()
        raise HTTPException(status_code=403, detail="Access Denied. Root administrative clearance required.")
        
    cursor.execute("SELECT username FROM users WHERE id = ?", (target_user_id,))
    target = cursor.fetchone()
    if not target:
        conn.close()
        raise HTTPException(status_code=404, detail="Target user profile structure not found.")
        
    alphabet = string.ascii_letters + string.digits + "!@#$%^&*()_+-="
    new_password = ''.join(secrets.choice(alphabet) for _ in range(16))
    hashed_pwd = hash_password(new_password)
    
    try:
        cursor.execute("UPDATE users SET password_hash = ? WHERE id = ?", (hashed_pwd, target_user_id))
        conn.commit()
    except Exception as e:
        conn.close()
        raise HTTPException(status_code=500, detail=f"Database mutation crash: {str(e)}")
        
    conn.close()
    return {
        "status": "Success",
        "message": f"Successfully regenerated login credentials matrix for '{target['username']}'.",
        "new_password": new_password
    }

@app.post("/admin/deposit/update-crypto")
def update_crypto_deposit_details(admin_id: int, crypto_name: str, network: str, address: str):
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT role FROM users WHERE id = ?", (admin_id,))
    operator = cursor.fetchone()
    if not operator or operator["role"] != "admin":
        conn.close()
        raise HTTPException(status_code=403, detail="Access Denied. Administrative clearance required.")
    conn.close()
    
    DEPOSIT_CONFIG["crypto"]["crypto_name"] = crypto_name
    DEPOSIT_CONFIG["crypto"]["network"] = network
    DEPOSIT_CONFIG["crypto"]["address"] = address
    return {"status": "Success", "message": "On-chain transaction parameters updated globally."}

@app.post("/admin/deposit/update-comms")
def update_administrative_communications_matrix(admin_id: int, whatsapp: str, telegram: str, gmail: str):
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT role FROM users WHERE id = ?", (admin_id,))
    operator = cursor.fetchone()
    if not operator or operator["role"] != "admin":
        conn.close()
        raise HTTPException(status_code=403, detail="Access Denied. Administrative clearance required.")
    conn.close()
    
    DEPOSIT_CONFIG["comms"]["whatsapp_phone"] = whatsapp
    DEPOSIT_CONFIG["comms"]["telegram_bot"] = telegram
    DEPOSIT_CONFIG["comms"]["gmail_address"] = gmail
    return {"status": "Success", "message": "Global agent communication channel matrices successfully aligned."}

@app.post("/admin/upload")
async def admin_upload_file(
    title: str = Form(...),
    description: str = Form(...),
    base_price: float = Form(...),
    collection: str = Form(...),
    file: UploadFile = File(...)
):
    STORAGE_DIR = "storage"
    if not os.path.exists(STORAGE_DIR):
        os.makedirs(STORAGE_DIR)
        
    secure_filename = file.filename.replace(" ", "_") 
    destination_path = os.path.join(STORAGE_DIR, secure_filename)
    
    try:
        contents = await file.read()
        with open(destination_path, "wb") as f:
            f.write(contents)
    except Exception:
        raise HTTPException(status_code=500, detail="Failed to write file stream to server storage disk.")
        
    conn = get_db_connection()
    cursor = conn.cursor()
    try:
        cursor.execute(
            """INSERT INTO files (title, description, base_price, secure_file_path, collection) 
               VALUES (?, ?, ?, ?, ?)""",
            (title, description, base_price, destination_path, collection)
        )
        conn.commit()
    except sqlite3.Error as e:
        conn.close()
        raise HTTPException(status_code=500, detail=f"Database logging failure: {str(e)}")
        
    conn.close()
    return {"status": "Success", "message": f"Asset '{title}' deployed to collection folder [{collection}] successfully."}

@app.get("/wallet/balance/{user_id}")
def get_wallet_balance(user_id: int):
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT username, wallet_balance FROM users WHERE id = ?", (user_id,))
    user = cursor.fetchone()
    conn.close()
    if not user:
        raise HTTPException(status_code=404, detail="User not found.")
    return {
        "username": user["username"],
        "wallet_balance": float(user["wallet_balance"])
    }

@app.post("/wallet/deposit")
def deposit_funds(deposit: WalletDeposit):
    if deposit.amount <= 0:
        raise HTTPException(status_code=400, detail="Deposit amount must be greater than zero.")
        
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute("SELECT wallet_balance FROM users WHERE id = ?", (deposit.user_id,))
    user = cursor.fetchone()
    
    if not user:
        conn.close()
        raise HTTPException(status_code=404, detail="User not found.")
        
    current_balance = float(user["wallet_balance"])
    new_balance = current_balance + deposit.amount
    
    try:
        cursor.execute("UPDATE users SET wallet_balance = ? WHERE id = ?", (new_balance, deposit.user_id))
        conn.commit()
    except Exception as e:
        conn.close()
        raise HTTPException(status_code=500, detail=f"Database error: {str(e)}")
        
    conn.close()
    return {
        "message": "Deposit successful!",
        "user_id": deposit.user_id,
        "amount_added": deposit.amount,
        "new_balance": new_balance
    }

@app.get("/files/search")
def search_catalog_files(query: str = "", collection: str = "all"):
    """Searches the database catalog, filtering dynamically by text patterns and collection folders."""
    conn = get_db_connection()
    cursor = conn.cursor()
    search_pattern = f"%{query}%"
    
    if collection == "all":
        cursor.execute(
            "SELECT id, title, description, base_price, collection FROM files WHERE title LIKE ? OR description LIKE ?", 
            (search_pattern, search_pattern)
        )
    else:
        cursor.execute(
            "SELECT id, title, description, base_price, collection FROM files WHERE (title LIKE ? OR description LIKE ?) AND collection = ?", 
            (search_pattern, search_pattern, collection)
        )
        
    matching_files = cursor.fetchall()
    conn.close()
    
    return [
        {
            "id": file["id"],
            "title": file["title"],
            "description": file["description"],
            "base_price": float(file["base_price"]),
            "collection": file["collection"]
        } for file in matching_files
    ]

@app.post("/files/add")
def add_file_to_catalog(file_data: FileCreate):
    """Alternative programmatic route to map references directly to external catalog files data."""
    conn = get_db_connection()
    cursor = conn.cursor()
    try:
        cursor.execute(
            "INSERT INTO files (title, description, base_price, secure_file_path, collection) VALUES (?, ?, ?, ?, ?)",
            (file_data.title, file_data.description, file_data.base_price, file_data.secure_file_path, file_data.collection)
        )
        conn.commit()
        file_id = cursor.lastrowid
    except Exception as e:
        conn.close()
        raise HTTPException(status_code=500, detail=f"Database error while adding file: {str(e)}")
    conn.close()
    return {
        "message": "File added to catalog successfully!",
        "file_id": file_id,
        "base_price": file_data.base_price
    }