
import ollama

from pathlib import Path
import uuid
import json
import asyncio
from enum import Enum
from fastapi import HTTPException
from pydantic import BaseModel, Field, validator
from typing import List, Dict, TypedDict, Literal, Any
import numpy as np
np.random.seed(42)
from src.utils import initialize_a_logger
from src.structure_analysis import StructureAnalyzer


class PromptRequest(BaseModel):
    prompt: str

class ComplianceRequest(BaseModel):
    external_requirements: List[str]
    internal_requirements: List[str]


class Compliance_Verifier(object):
    def __init__(self, config=None):
        self.config = config
        self.logger = initialize_a_logger('./logs/ComplianceVerifier.log')
        self.logger.debug(f"Compliance start")
        self.structure_analyzer = StructureAnalyzer()

    async def generate_text(self, req: PromptRequest):
        """
        Sends a prompt to the Qwen 2.5 3B model running on the local Ollama server.
        """
        model_name = "qwen2.5:3b"
        
        try:
            response = ollama.chat(
                model=model_name,
                messages=[{'role': 'user', 'content': req.prompt}],
                options={"temperature": 0.0},
            )

            content = response.get("message", {}).get("content", "Error: No content found in response.")
            
            return {"oss_json": content}        
        except Exception as e:
            # Raising an HTTPException will return the error to the user via FastAPI
            error_message = str(e)
            self.logger.error(f"Error generating text: {error_message}")
            raise HTTPException(status_code=500, detail=error_message) 

    async def vectorize_requirement(self, req: PromptRequest):
        self.logger.debug(f"embed req : {req }")
        try:
            response = ollama.embeddings(
                model="embeddinggemma:300m",
                prompt=req
            )
            
            embedding = response["embedding"]
            self.logger.debug(f"embedding : {embedding }")
            
            return {
                "embedding_preview": embedding,  
                "total_dimensions": len(embedding),
                "model": "embeddinggemma:300m"
            }
            
        except Exception as e:
            self.logger.debug(f"embedding : {embedding }")
            return {"error": str(e)}

    async def structural_similarity(self, data: ComplianceRequest) -> Dict[str, Any]:
        req1 = " ".join(data.external_requirements)
        req2 = " ".join(data.internal_requirements)
        self.logger.debug(f"req1 : {req1 }")
        self.logger.debug(f"req2 : {req2}")
        try:
            keywords_req1 = self.structure_analyzer.extract_keywords(req1)
            keywords_req2 = self.structure_analyzer.extract_keywords(req2)

            self.logger.debug(f"keywords_req1 : {keywords_req1 }")
            self.logger.debug(f"keywords_req2 : {keywords_req2}")

            common = list(set(keywords_req1) & set(keywords_req2))
            self.logger.debug(f"common : {common}")

            return {
                "keywords_req1": keywords_req1,
                "keywords_req2": keywords_req2,
                "structure_match": common
            }
        except Exception as e:
            self.logger.info(f"Failed to log structure_match: {e}")
            return {
                "keywords_req1": [],
                "keywords_req2": [],
                "structure_match": -1
            }

    async def semantic_similarity(self, data: ComplianceRequest) -> Dict[str, Any]:
        req1 = " ".join(data.external_requirements)
        req2 = " ".join(data.internal_requirements)
        self.logger.debug(f"req1 : {req1 }")
        self.logger.debug(f"req2 : {req2}")
        try:
            
            result1 = await self.vectorize_requirement(req1)
            result2 = await self.vectorize_requirement(req2)
            self.logger.debug(f"result1 : {result1 }")
            self.logger.debug(f"result2 : {result2 }")
            if "error" in result1 or "error" in result2:
                return {
                    "error": "Failed to generate embeddings",
                    "result1": result1,
                    "result2": result2
                }

            emb1 = np.array(result1.get('embedding_preview', []))
            emb2 = np.array(result2.get('embedding_preview', []))
            self.logger.debug(f"emb1: {emb1.shape }")
            self.logger.debug(f"emb2: {emb2.shape }")
            similarity = None
            if len(emb1) > 0 and len(emb2) > 0:
                similarity = np.dot(emb1, emb2) / (np.linalg.norm(emb1) * np.linalg.norm(emb2)) +0.000000001
                self.logger.debug(f"CustomMCP: {similarity}")
                self.logger.info(f"similarity_score {similarity}")
            return {
                "requirement1": req1,
                "requirement2": req2,
                "similarity_score": float(similarity) if similarity is not None else None
            }
        except Exception as e:
            self.logger.info(f"Failed to log similarity_score: {e}")
            return {
                "requirement1": req1,
                "requirement2": req2,
                "similarity_score": -1
            }