import os
from typing import Optional
import json
import httpx
from fastapi import HTTPException, status
from sqlalchemy import Delete, desc, Select, select, func
from sqlalchemy.orm import selectinload

from src.aeros_equipment.service import save_default_equipment
from src.aeros_simulation.service import save_default_simulation_node
from src.auth.service import CurrentUser
from src.config import WINDOWS_AEROS_BASE_URL, CLAMAV_HOST, CLAMAV_PORT
from src.aeros_utils import aeros_post, aeros_file_upload
from src.database.core import DbSession
from src.database.service import search_filter_sort_paginate
from src.utils import sanitize_filename
import clamd
import io

from .model import AerosProject
from .schema import AerosProjectInput, OverhaulScheduleCreate, OverhaulScheduleUpdate
import asyncio
ALLOWED_EXTENSIONS = {".aro"}
MAX_FILE_SIZE = 100 * 1024 * 1024  # 100MB
# client = httpx.AsyncClient(timeout=300.0) # Managed in src.aeros_utils
# We still use a local client for WINDOWS_AEROS_BASE_URL if it's not managed by aeros_utils
client = httpx.AsyncClient(timeout=300.0)


async def import_aro_project(*, db_session: DbSession, aeros_project_in: AerosProjectInput):
    # windows_aeros_base_url = WINDOWS_AEROS_BASE_URL


    file = aeros_project_in.aro_file
    
    # Sanitize and validate filename
    try:
        clean_filename = sanitize_filename(file.filename)
    except ValueError as e:
        raise HTTPException(
            status_code=400,
            detail=f"Invalid filename: {str(e)}"
        )

    # Check if mime type is application/octet-stream 
    if file.content_type != "application/octet-stream":
        raise HTTPException(
            status_code=400,
            detail="Invalid file type. Allowed: application/octet-stream"
        )


    # Get filename
    filename_without_ext = os.path.splitext(clean_filename)[0]

    # Get file extension
    file_ext = os.path.splitext(clean_filename)[1].lower()

    # Validate file extension
    if file_ext not in ALLOWED_EXTENSIONS:
        raise HTTPException(
            status_code=400,
            detail=f"File type not allowed. Allowed: {ALLOWED_EXTENSIONS}"
        )

    print("read file")


    # Read and check file size
    content = await file.read()
    if len(content) > MAX_FILE_SIZE:
        raise HTTPException(
            status_code=400,
            detail="File too large. Max size: 100Mb"
        )

    # ClamAV Scan
    try:
        cd = clamd.ClamdNetworkSocket(CLAMAV_HOST, CLAMAV_PORT)
        scan_result = cd.instream(io.BytesIO(content))
        # Result format: {'stream': ('FOUND', 'Eicar-Test-Signature')} or {'stream': ('OK', None)}
        if scan_result and scan_result.get('stream') and scan_result['stream'][0] == 'FOUND':
            raise HTTPException(
                status_code=400,
                detail=f"Virus detected: {scan_result['stream'][1]}"
            )
    except clamd.ConnectionError:
        pass
    # except HTTPException:
    #     raise
    # except Exception as e:
    #     print(f"ClamAV error: {e}")
    #     raise HTTPException(
    #         status_code=500,
    #         detail=f"Antivirus check failed: {str(e)}"
    #     )


    # Project name hardcode
    # project_name = "trialapi"

    # Project name
    project_name = filename_without_ext


    ## save File to windows app
    # Output is string of file path, examole
    # # Example response "C/dsad/dsad.aro"
    try:
        # Reset file position since we already read it for size check
        # await file.seek(0)

        # Prepare file for upload
        # files = {
        #     "file": (clean_filename, content, file.content_type or "application/octet-stream")
        # }

        response = await aeros_file_upload(
            "/upload",
            content,
            "file",
            clean_filename,
            base_url=WINDOWS_AEROS_BASE_URL,
        )

        response.raise_for_status()

        # print("fetch")
        # response = await client.post(
        #     f"{WINDOWS_AEROS_BASE_URL}/upload-file",
        #     files=files
        # )
        # response.raise_for_status()

        # Get the file path from the response
        upload_result = response.json()
        aro_path = upload_result.get("full_path")
        filename = upload_result.get("stored_filename").replace(".aro", "")

        if not aro_path:
            raise HTTPException(
                status_code=500,
                detail="Failed to get file path from upload response"
            )

    except httpx.HTTPStatusError as e:
        raise HTTPException(
            status_code=e.response.status_code,
            detail=f"Upload failed: {e.response.text}"
        )
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
        )

    await asyncio.sleep(2)

    # aro_path = r"C:/Users/user/Documents/Aeros/sample_project.aro"

    aeros_project = AerosProject(project_name=filename, aro_file_path=aro_path)

    # find aeros record first, if not found, then create a new one
    stmt = select(AerosProject).order_by(desc(AerosProject.created_at)).limit(1)
    result = await db_session.execute(stmt)
    latest_project = result.scalar_one_or_none()

    # If aeros record found, then update it
    if latest_project:
        latest_project.project_name = filename
        latest_project.aro_file_path = aro_path
    else: # else create new aeros record
        db_session.add(aeros_project)

    await db_session.commit()

    # aro_json = json.dumps(aro_path)

    # Update path to AEROS APP
    # Example BODy "C/dsad/dsad.aro"
    try:
        response = await aeros_post(
            "/api/Project/ImportAROFile",
            json=aro_path,
            headers={"Content-Type": "application/json"},
        )
        
        response.raise_for_status() 
        response_json = response.json()
        
        
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
        )

    await _initialize_default_project_data(
            db_session=db_session,
            project_name=filename
    )

async def fetch_aro_record(*, db_session: DbSession):
    stmt = select(AerosProject).order_by(desc(AerosProject.updated_at)).limit(1)
    result = await db_session.execute(stmt)
    found_record = result.scalar_one_or_none()

    return found_record

async def get_project(*, db_session: DbSession):
    stmt = select(AerosProject).order_by(desc(AerosProject.updated_at)).limit(1)
    result = await db_session.execute(stmt)
    found_record = result.scalar_one_or_none()

    return found_record

async def _initialize_default_project_data(
    *,
    db_session: DbSession,
    project_name: str
) -> None:
    """
    Initialize default equipment and simulation nodes for a project.

    Args:
        db_session: Database session
        project_name: Name of the project to initialize
    """
    try:
        # Save default equipment
        await save_default_equipment(
            db_session=db_session,
            project_name=project_name
        )

        # # Save default simulation node
        # await save_default_simulation_node(
        #     db_session=db_session,
        #     project_name=project_name
        # )

        await db_session.commit()

    except Exception as e:
        await db_session.rollback()
        raise e


async def reset_project(*, db_session: DbSession):
    project = await fetch_aro_record(db_session=db_session)

    try:
        response = await aeros_post(
            "/api/Project/ImportAROFile",
            data=f'"{project.aro_file_path}"',
            headers={"Content-Type": "application/json"},
        )
        response.raise_for_status()
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
        )

    return project.aro_file_path


async def create(*, db_session: DbSession, overhaul_job_in: OverhaulScheduleCreate):
    # Placeholder for creation logic
    print(f"create overhaul job called: {overhaul_job_in}")
    pass

async def update(*, db_session: DbSession, overhaul_schedule_id: str, overhaul_job_in: OverhaulScheduleUpdate):
    # Placeholder for update logic
    print(f"update overhaul job called for id {overhaul_schedule_id}: {overhaul_job_in}")
    pass

async def delete(*, db_session: DbSession, overhaul_schedule_id: str):
    # Placeholder for delete logic
    print(f"delete overhaul job called for id {overhaul_schedule_id}")
    pass
