"""FastAPI Web Server for remote CLI command execution"""

import os
import asyncio
from typing import AsyncIterator
import uvicorn
from fastapi import FastAPI
from fastapi.responses import StreamingResponse

from .constants import DEFAULT_WEB_ADDR, DEFAULT_WEB_PORT, ENV_WEB_ADDR, ENV_WEB_PORT
from .models import CommandRequest, CommandResponse
from diona.project.encrypt import get_crypto_manager
from diona.project.cli.engine import get_engine
from diona.project.cli.engine.result import StreamChunk


app = FastAPI(title="Diona CLI Web Server", version="1.0.0")


def get_web_addr() -> str:
    return os.environ.get(ENV_WEB_ADDR, DEFAULT_WEB_ADDR)


def get_web_port() -> int:
    port_str = os.environ.get(ENV_WEB_PORT, str(DEFAULT_WEB_PORT))
    try:
        return int(port_str)
    except ValueError:
        return DEFAULT_WEB_PORT


async def execute_command_stream(command: str, crypto) -> AsyncIterator[str]:
    """Execute command and yield streaming responses"""
    engine = get_engine()

    async def generate():
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            async for chunk in engine.execute(command):
                if chunk.is_error:
                    encrypted = crypto.encrypt(chunk.content)
                    yield f"error: {encrypted}\n\n"
                else:
                    encrypted = crypto.encrypt(chunk.content)
                    yield f"data: {encrypted}\n\n"

                if chunk.is_final:
                    yield f"done: returncode=0\n\n"

        except Exception as e:
            encrypted = crypto.encrypt(str(e))
            yield f"error: {encrypted}\n\n"
        finally:
            loop.close()

    return generate()


@app.on_event("startup")
async def startup_event():
    """Initialize engine on startup"""
    get_engine()


@app.get("/")
async def root():
    """Root endpoint"""
    return {"message": "Diona CLI Web Server is running", "version": "1.0.0"}


@app.get("/health")
async def health():
    """Health check endpoint"""
    return {"status": "healthy"}


@app.post("/execute", response_model=CommandResponse)
async def execute_command(request: CommandRequest):
    """Execute a command and return the result"""
    crypto = get_crypto_manager()
    engine = get_engine()

    if not request.command or not request.command.strip():
        return CommandResponse(
            success=False,
            return_code=-1,
            stdout="",
            stderr="",
            error_message="Empty command"
        )

    command = request.command.strip()

    try:
        decrypted_command = crypto.decrypt(command)
    except Exception:
        decrypted_command = command

    stdout = ""
    stderr = ""
    success = True
    return_code = 0

    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        async for chunk in engine.execute(decrypted_command):
            if chunk.is_error:
                stderr += chunk.content
                success = False
            else:
                stdout += chunk.content
    except Exception as e:
        stderr += str(e)
        success = False
    finally:
        loop.close()

    return CommandResponse(
        success=success,
        return_code=return_code,
        stdout=stdout,
        stderr=stderr,
        error_message=None
    )


@app.post("/execute/stream")
async def execute_command_stream_endpoint(request: CommandRequest):
    """Execute a command and stream the result in real-time"""
    crypto = get_crypto_manager()

    if not request.command or not request.command.strip():
        return CommandResponse(
            success=False,
            return_code=-1,
            stdout="",
            stderr="",
            error_message="Empty command"
        )

    command = request.command.strip()

    try:
        decrypted_command = crypto.decrypt(command)
    except Exception:
        decrypted_command = command

    engine = get_engine()

    async def generate():
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            async for chunk in engine.execute(decrypted_command):
                if chunk.is_error:
                    encrypted = crypto.encrypt(chunk.content)
                    yield f"error: {encrypted}\n\n"
                else:
                    encrypted = crypto.encrypt(chunk.content)
                    yield f"data: {encrypted}\n\n"

                if chunk.is_final:
                    yield f"done: returncode=0\n\n"

        except Exception as e:
            encrypted = crypto.encrypt(str(e))
            yield f"error: {encrypted}\n\n"
        finally:
            loop.close()

    return StreamingResponse(generate(), media_type="text/event-stream")


def run_server():
    """Run the web server"""
    addr = get_web_addr()
    port = get_web_port()

    print(f"Starting Diona CLI Web Server at http://{addr}:{port}")
    uvicorn.run(app, host=addr, port=port)


if __name__ == "__main__":
    run_server()
