import os
from fastapi import APIRouter, Depends, HTTPException, Request, Body
from typing import Dict
from pydantic import BaseModel
from .ConfigurationLoader import ConfigurationLoader
from .AuthenticationHandler import AuthenticationHandler
from .APIRequestBuilder import APIRequestBuilder
from .ResponseHandler import ResponseHandler
from .IntegrationManager import IntegrationManager
from starlette.concurrency import run_in_threadpool
from datetime import datetime
from httpx import Response
from app.db import database  # Ensure this is your actual database module
from app.api.v1.libraries.object import str_to_objectid  # Ensure this is your actual utility module
from jinja2 import Environment, FileSystemLoader, select_autoescape

current_dir = os.path.dirname(os.path.realpath(__file__))
templates_dir = os.path.join(current_dir, "sdk", "templates")


APP_COLLECTION = "app"
APP_INTEGRATION_COLLECTION = "app_users"
APP_RESPONSE_TEMPLATES_COLLECTION = "app_response_templates"
GOAL_COLLECTION = "goals"
PROJECTFLOW_COLLECTION = "project_flows"
CHATS_COLLECTION = "chats"

class AppFunctionRequest(BaseModel):
    projectid: str
    visitorid: str
    flowid: str
    appintegrationid: str
    appid: str
    interactionid: str
    visitorname: str
    requestData: dict = {}  # Assuming requestData is a dictionary


router = APIRouter()

#@router.post("/{projectid}/{visitorid}/{flowid}/{appintegrationid}/")
#async def execute_app_function(projectid: str, visitorid: str, appintegrationid: str, flowid: str, request_data: Dict = Body(...), db: database.MongoDB = Depends(database.get_mongo_db)):
@router.post("/")
async def app_call(request_body: AppFunctionRequest, db: database.MongoDB = Depends(database.get_mongo_db)):
    config_loader = ConfigurationLoader(db)
    auth_handler = AuthenticationHandler()
    request_builder = APIRequestBuilder()
    response_handler = ResponseHandler()
    integration_manager = IntegrationManager(config_loader, auth_handler, request_builder, response_handler)

    projectid = request_body.projectid
    visitorid = request_body.visitorid
    flowid = request_body.flowid
    interactionid = request_body.interactionid
    visitorname = request_body.visitorname
    appintegrationid = request_body.appintegrationid
    appid = request_body.appid
    request_data = request_body.requestData

    #request_data = await extract_request_data(request)

    await update_visitor_information(db, visitorid, request_data,interactionid, visitorname)

    # Verify project flow
    if not await verify_project_flow(db, projectid, flowid):
        raise HTTPException(status_code=400, detail="Invalid project flow")
    
    try:
        response, response_schema = await integration_manager.execute_integration(appintegrationid, flowid, request_data) #run_in_threadpool()
        await insert_goal(db, projectid, visitorid, appintegrationid, flowid, request_data, response)
        template_ref = response_schema.get('x-template', '')
        template_content = None
        if(template_ref):
            template_content = await fetch_template(appid, template_ref, db)
        app_response = await response_handler.handle_response(response, response_schema, template_content)

        final_response = {
            "message": app_response
        }
        return final_response
    
    except Exception as e:
        print(f"ERRROR boase {str(e)} ")
        raise HTTPException(status_code=500, detail=f"Integration execution error: {str(e)}")

@router.post("/flowtest/")
async def app_call(request_body: AppFunctionRequest, db: database.MongoDB = Depends(database.get_mongo_db)):
    config_loader = ConfigurationLoader(db)
    auth_handler = AuthenticationHandler()
    request_builder = APIRequestBuilder()
    response_handler = ResponseHandler()
    integration_manager = IntegrationManager(config_loader, auth_handler, request_builder, response_handler)

    flowid = request_body.flowid
    appintegrationid = request_body.appintegrationid
    appid = request_body.appid
    request_data = request_body.requestData

    try:
        response, response_schema = await integration_manager.execute_integration(appintegrationid, flowid, request_data) #run_in_threadpool()
        template_ref = response_schema.get('x-template', '')
        template_content = None
        if(template_ref):
            template_content = await fetch_template(appid, template_ref, db)
        app_response = await response_handler.handle_response(response, response_schema, template_content)

        final_response = {
            "message": app_response
        }
        return final_response
    
    except Exception as e:
        print(f"ERRROR boase {str(e)} ")
        raise HTTPException(status_code=500, detail=f"Integration execution error: {str(e)}")

async def verify_project_flow(db, projectid, flowid):
    project_flow_collection = db.get_collection(PROJECTFLOW_COLLECTION)
    return project_flow_collection.find_one({"project_id": projectid, "flow_id": flowid}) is not None

async def insert_goal(db, projectid, visitorid, appintegrationid, flowid, query_params, response):
    goal_collection = db.get_collection(GOAL_COLLECTION)
    response_data = response
    # Extract relevant information from the response object
    if isinstance(response, Response):
        # It's an HTTP response
        response_data = {
            "status_code": response.status_code,
            "body": response.json() if response.headers.get('Content-Type', '') == 'application/json' else response.text
        }
    elif isinstance(response, dict):
        # It's a dictionary response from SDK
        response_data = response

    goal_data = {
        "project_id": projectid,
        "visitor_id": visitorid,
        "app_integration_id": appintegrationid,
        "flow_id": flowid,
        "timestamp": datetime.now(),
        "query_params": query_params,
        "response": response_data,
    }
    try:
        goal_collection.insert_one(goal_data)
    except:
        print("Goal is not inserted! ")

async def extract_request_data(request: Request):
    if request.method == "GET":
        return dict(request.query_params)
    elif request.method == "POST":
        return await request.json()
    else:
        raise HTTPException(status_code=405, detail="Method not allowed")

async def update_visitor_information(db, visitorid, data, interactionid, visitorname):
    # Normalize field names

    data = {k.lower(): v for k, v in data.items()}

    normalized_data = {
        "name": data.get("name") or data.get("fullname") or data.get("full_name") or data.get("last_name") or data.get("lastname"),
        "email": data.get("email") or data.get("email_address"),
        "mobile": data.get("mobile") or data.get("phone") or data.get("mobilenumber") or data.get("phonenumber") or data.get("mobile_number") or data.get("phone_number"),
        "company_name": data.get("company") or data.get("companyname") or data.get("company_name"),
    }

    visitors_collection = db.get_collection("visitors")  # Replace with your actual visitors collection name
    update_data = {k: v for k, v in normalized_data.items() if v is not None}
    if update_data:
        visitors_collection.update_one(
            {"_id": str_to_objectid(visitorid)},
            {"$set": update_data}
        )

    formatted_message = " <<Submitted Data>> : " + ", ".join([f"{k.capitalize()}: {v}" for k, v in data.items()])

    # Prepare the user message structure
    user_msg_structure = {
        "sender": visitorname,
        "message": formatted_message,
        "context": "",
        "type": "user",
        "timestamp": datetime.now(),
        "interaction_id": interactionid
    }

    # Insert the user message into the chats collection
    chats_collection = db.get_collection(CHATS_COLLECTION)
    chats_collection.insert_one(user_msg_structure)

async def fetch_template(appid, template_ref, db):
    if template_ref.startswith("db:"):
        # Extract the actual template name
        template_name = template_ref.split("db:", 1)[1]
        return await fetch_template_database(db, appid, template_name)
    else:
        # Load from the filesystem
        return await fetch_template_filesystem(templates_dir, template_ref)

async def fetch_template_database(db, appid, template_name):
    try:
        # Assuming db is an instance of a database client and you have a method to get a collection
        templates = db.get_collection(APP_RESPONSE_TEMPLATES_COLLECTION)
        # Query the database for the template
        template_details = templates.find_one({"app_id": appid, "template_name": template_name})
        if template_details:
            return template_details.get('template_content')  # Assuming the content is stored under 'template_content'
        else:
            print("Template not found in database.")
            return None
    except Exception as e:
        print(f"Error fetching template from database: {e}")
        return None

async def fetch_template_filesystem(templates_dir, template_ref):
    try:
        # Construct the full path to the template file
        template_path = os.path.join(templates_dir, template_ref)
        # Open and read the template file
        with open(template_path, 'r', encoding='utf-8') as file:
            template_content = file.read()
        return template_content
    except FileNotFoundError as e:
        print(f"Template file not found: {e}")
        return None
    except Exception as e:
        print(f"Error loading template: {e}")
        return None
