from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, validator
from app.services.speckle_service import SpeckleService
from specklepy.objects import Base
from datetime import datetime
from typing import List, Any
import json

class GenericPayload(BaseModel):
    payload: str

    @validator("payload")
    def validate_payload(cls, value):
        # Parse the JSON string to ensure it's valid
        try:
            parsed = json.loads(value)
            # You can add additional validation here if needed
        except json.JSONDecodeError:
            raise ValueError("payload must be a valid JSON string")
        return value

    def get_data(self):
        return json.loads(self.payload)

router = APIRouter()

def extract_value(obj: Any, key: str) -> Any:
    """Recursively search for the value of a given key in nested objects"""
    print(f"Extracting value for key: {key} from object: {obj}")
    if isinstance(obj, dict):
        for k, v in obj.items():
            print(f"Checking key: {k}, value: {v}")
            if k == key:
                print(f"Found key: {k}, returning value: {v}")
                return v
            elif isinstance(v, (dict, list)):
                result = extract_value(v, key)
                if result is not None:
                    return result
    elif isinstance(obj, list):
        for item in obj:
            result = extract_value(item, key)
            if result is not None:
                return result
    return None

@router.post("/createDistanceMatrix")
async def create_distance_matrix(payload: GenericPayload):
    try:
        data = payload.get_data()

        # Extract necessary fields from the payload
        speckle_input_graph = extract_value(data, 'speckleInput_Graph')
        speckle_input_buildings = extract_value(data, 'speckleInput_Buildings')
        speckle_output_distance_matrix = extract_value(data, 'speckleOutput_DistanceMatrix')
        token = extract_value(data, 'token')
        modal_mode = extract_value(data, 'modal_mode')

        # Step 1: Create the client
        client = SpeckleService.create_client(base_url="https://speckle.xyz", token=token)

        # Step 2: Parse the Speckle URLs to get the components
        graph_speckle_url_info = SpeckleService.parse_speckle_url(speckle_input_graph)
        graph_stream_id = graph_speckle_url_info["streamID"]
        graph_branch_name = graph_speckle_url_info["branchName"]

        buildings_speckle_url_info = SpeckleService.parse_speckle_url(speckle_input_buildings)
        buildings_stream_id = buildings_speckle_url_info["streamID"]
        buildings_branch_name = buildings_speckle_url_info["branchName"]

        output_speckle_url_info = SpeckleService.parse_speckle_url(speckle_output_distance_matrix)
        output_stream_id = output_speckle_url_info["streamID"]
        output_branch_name = output_speckle_url_info["branchName"]

        # Step 3: Fetch the Speckle branches
        graph_data, graph_commit_id = SpeckleService.get_speckle_stream(graph_stream_id, graph_branch_name, client)
        buildings_data, buildings_commit_id = SpeckleService.get_speckle_stream(buildings_stream_id, buildings_branch_name, client)

        # Step 4: Generate metadata
        metadata = SpeckleService.generate_metadata([speckle_input_graph, speckle_input_buildings], token)

        # Step 5: Create a dummy Speckle object
        dummy_object = Base()
        dummy_object["IWasCreatedBy"] = "createDistanceMatrix"
        dummy_object["ModalMode"] = modal_mode
        dummy_object["MetaData"] = metadata

        # Step 6: Update the Speckle stream
        new_commit_id = SpeckleService.update_speckle_stream(output_stream_id, output_branch_name, client, dummy_object)

        # Step 7: Create the report using metadata
        current_time = datetime.utcnow().isoformat()
        sources = [
            {
                "streamID": graph_stream_id,
                "branchName": graph_branch_name,
                "commitID": graph_commit_id,
                "time": current_time
            },
            {
                "streamID": buildings_stream_id,
                "branchName": buildings_branch_name,
                "commitID": buildings_commit_id,
                "time": current_time
            }
        ]
        targets = [
            {
                "streamID": output_stream_id,
                "branchName": output_branch_name,
                "commitID": new_commit_id,
                "time": current_time
            }
        ]
        report = {
            "method": "createDistanceMatrix",
            "sources": sources,
            "targets": targets
        }

        return report

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))