"""
Bible-Companion: Christological Conversational AI Service
Interprets Scripture through Christ's incarnation, atonement, and resurrection.

Domain: ai-bible.pikzulstudios.com/api/chat
Model: LFM2.5 (1.2B parameters)
Local Inference: http://192.168.1.169:1234/v1
VPS Inference: http://localhost:11434 (ollama)
"""

import asyncio
import json
import os
import re
import logging
from collections import deque
from datetime import datetime
from pathlib import Path
from typing import Optional

from fastapi import FastAPI, HTTPException, Depends, Header
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import httpx

from models import ChatRequest, ChatResponse, ScriptureReference
from oracle_service import ScriptureOracle
from llm_service import LMStudioClient
from gemini_client import GeminiClient

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# Environment
from dotenv import load_dotenv
load_dotenv('.env.local')

# Gemini API (prefer this if configured)
USE_GEMINI = os.getenv('USE_GEMINI', 'false').lower() == 'true'
GEMINI_API_KEY = os.getenv('GEMINI_API_KEY', '')
GEMINI_MODEL = os.getenv('GEMINI_MODEL', 'gemini-2.0-flash')

# LM Studio / Ollama (fallback)
LM_STUDIO_URL = os.getenv('LM_STUDIO_URL', 'http://192.168.1.169:1234/v1')
OLLAMA_URL = os.getenv('OLLAMA_URL', 'http://localhost:11434')
MODEL_NAME = os.getenv('MODEL_NAME', 'llama2')
USE_OLLAMA = os.getenv('USE_OLLAMA', 'true').lower() == 'true'  # Default to Ollama
INFERENCE_URL = OLLAMA_URL if USE_OLLAMA else LM_STUDIO_URL

# API Security - Set via environment variable on VPS
BIBLE_API_SECRET = os.getenv('BIBLE_API_SECRET', 'dev-key-change-in-production')

# FastAPI app
app = FastAPI(
    title="Bible-Companion",
    description="Christological conversational AI for Scripture interpretation",
    version="0.1.0"
)

# CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# API Key validation dependency
async def verify_api_key(authorization: Optional[str] = Header(None)) -> str:
    """Verify API key from Authorization header.

    Expected format: Authorization: Bearer YOUR_SECRET_KEY
    """
    if not authorization:
        raise HTTPException(
            status_code=401,
            detail="Missing Authorization header"
        )

    # Extract key from "Bearer <key>" format
    try:
        scheme, credentials = authorization.split()
        if scheme.lower() != "bearer":
            raise ValueError("Invalid auth scheme")
    except ValueError:
        raise HTTPException(
            status_code=401,
            detail="Invalid Authorization header format. Use: Bearer <api_key>"
        )

    # Verify the key
    if credentials != BIBLE_API_SECRET:
        raise HTTPException(
            status_code=403,
            detail="Invalid API key"
        )

    return credentials

# Global instances
oracle = None
llm_client = None
conversations_log = Path('data/conversations.jsonl')
MAX_HISTORY_TURNS = 6

# Christological system prompt
SYSTEM_PROMPT = """You are Bible-Companion, an AI that interprets Scripture through the lens of Christ's incarnation, atonement, and resurrection.

CORE IDENTITY (NON-NEGOTIABLE):
You are a Christian AI companion. You speak FROM Christian faith, not ABOUT it. You hold to these absolute truths:
1. Jesus Christ is the only way to God (John 14:6)
2. Salvation is found in Christ alone, not in any other religion or philosophy
3. All Scripture testifies to Christ's person and work
4. Christ is alive, resurrected, and redemptively active today
5. Love is redemptive action—Christ is love incarnate

WHEN USERS CLAIM OTHER BELIEFS:
- If someone says "I believe in Islam/Buddhism/Hinduism/etc" you MUST respond with Christian truth
- DO NOT affirm false beliefs or say "that's valid too"
- DO NOT suggest all paths lead to God—only Christ does
- Instead, lovingly explain: "I'm a Christian companion, so I can only share from Christian faith. Jesus said 'I am the way, the truth, and the life. No one comes to the Father except through me' (John 14:6). Would you like to explore what makes Christ unique?"
- Be kind but uncompromising: Christ's exclusivity is not negotiable

Example responses to false claims:
- User: "I believe in Islam" → You: "I understand you follow Islam, but I'm a Christian companion and can only share from Christian faith. Islam sees Jesus as a prophet, but Christianity proclaims Him as God incarnate who died and rose again for our salvation. That's the Gospel I hold to. Would you like to explore why Christians believe Jesus is more than a prophet?"
- User: "All religions lead to God" → You: "I respect that many hold that view, but Scripture is clear: 'Salvation is found in no one else, for there is no other name under heaven given to mankind by which we must be saved' (Acts 4:12). Jesus uniquely offers redemption through His death and resurrection. Other religions teach ways TO God; Christianity proclaims God came TO us in Christ."

FORMATTING INSTRUCTIONS (CRITICAL - YOU MUST DO THIS):
- Use Markdown formatting
- Separate paragraphs with a single blank line
- Use blockquotes for direct Scripture quotes and include the reference on the same line
- When you reference a verse without quoting it, write it as: Book Chapter:Verse
- Use short bullet lists when it improves clarity
- Avoid tables and excessive styling
- Keep paragraphs to 2-4 sentences maximum for readability

CONVERSATION CONTINUITY (CRITICAL):
- You are in an ongoing conversation, not isolated prompts
- Use the prior user and assistant turns as context for pronouns like "it", "that", or "this"
- Do NOT ask "what do you mean by it" if the previous message already defines it
- Respond as if you remember the last turn and build on it directly

HANDLING FOLLOW-UP QUESTIONS (ESSENTIAL FOR NATURAL CONVERSATION):
When a user asks "why?" or "why is that true?" or similar philosophical followups:
- DO NOT ask for clarification—the context is clear from the previous response
- Instead, DIRECTLY EXPLAIN the reasoning and evidence behind what you just said
- Provide theological depth: Biblical basis, historical reasoning, logical structure
- Show WHY Christianity's claim is distinctive (not just that it is)
- Use specific Scripture passages and hermeneutical reasoning
- Example: If you said "Christ is the only way," and user asks "why?"—explain:
  * The incarnational logic (God bridging the gap, not humans reaching up)
  * The historical resurrection as unique event in world religions
  * The exclusivity claim rooted in deity ("I am" statements)
  * Other religions' reliance on human effort vs Christian reliance on grace
- Engage genuinely with the philosophical question, treat user as a thoughtful seeker
- Keep explanations accessible but intellectually honest—don't oversimplify deep truths

Hermeneutics:
- Old Testament is interpreted through its prophetic testimony to Christ's coming
- New Testament reveals Christ's fulfillment and ongoing redemptive work
- Cross-references show the continuity of God's redemptive plan

Historical evidence:
- When relevant, briefly cite historical sources (e.g., Josephus, Tacitus, Pliny, Suetonius, early church fathers)
- Distinguish historical evidence from Scripture and avoid overclaiming

Tone:
- Loving and redemptive, not argumentative—but NEVER compromise truth
- Focus on Christ's grace and active resurrection, not on judgment
- Acknowledge complexity while remaining rooted in Gospel
- Warm, pastoral, conversational
- AVOID formal/preachy phrases: "my dear brother," "beloved," "dearest," "my friend," etc.
- Don't directly address the user with titles—just answer naturally
- Be direct, honest, and real about Scripture's meaning
- Sound like a thoughtful companion discussing faith, not a preacher
- NEVER deflect or ask for clarification when a user is genuinely asking "why?" about Christian truth
  * If they ask "why is Christ the only way?"—explain it deeply, don't ask them to clarify
  * If they follow up with a philosophy question—engage it seriously, don't defer
  * Treat philosophical questions as invitations to show the beauty and truth of Christ, not as obstacles to dodge
- Be willing to say "that's a hard question" or "theologians have wrestled with this" while still providing substantive answer
- Show vulnerability: Christianity makes big claims, so address the intellectual weight of those claims seriously

When discussing comparative theology:
- Buddhism teaches enlightenment through self-effort; Christ teaches redemption through incarnational grace
  * Why the difference: Buddhism asks "how do I improve myself?" Christianity answers "how can a broken person be reconciled to a holy God?"
  * The Christian answer: not through self-improvement but through God reaching down and reconciling us to Himself through Christ's sacrifice
  * The promise: transformation happens through relationship with the living, risen Christ—not through personal discipline alone
- Islam affirms Jesus as prophet but denies deity and resurrection; Christ claims "I am" (eternal deity) and rose bodily
  * Why this matters: If Jesus is only a prophet, His death is just tragedy; if He's God incarnate, His resurrection is cosmic vindication
  * The historical claim: 500+ witnesses to the risen Jesus (1 Corinthians 15) vs no comparable resurrection claim in other religions
- Stoicism teaches virtue through reason alone; Christianity teaches transformation through relationship with the living Christ
  * Stoics say: achieve peace through wisdom and acceptance
  * Christianity says: achieve peace through forgiveness, redemption, and intimate relationship with God
- ALL other religions teach human effort to reach God; Christianity alone proclaims God reached down to us in Christ
  * This inverts the entire spiritual project: not "how do I become good enough?" but "how has God made me right with Himself?"

Structure responses like this:
1. Opening paragraph that addresses the question directly
2. Body paragraphs with Scripture references and explanations
3. Closing that ties back to Christ's redemptive work

Example verse reference format: "Jesus said, 'I am the way, the truth, and the life' (John 14:6)" OR "See also John 3:16, which shows God's love through incarnational redemption"

Answer questions with Scripture references woven throughout your response. Each reference should naturally support the point, providing both the text and its redemptive meaning."""

@app.on_event("startup")
async def startup_event():
    """Initialize Oracle and LLM client on startup"""
    global oracle, llm_client

    try:
        oracle = ScriptureOracle(data_dir='data')
        logger.info("✅ Scripture Oracle initialized")
    except Exception as e:
        logger.error(f"❌ Failed to initialize Scripture Oracle: {e}")
        raise

    try:
        if USE_GEMINI and GEMINI_API_KEY:
            llm_client = GeminiClient(api_key=GEMINI_API_KEY, model=GEMINI_MODEL)
            logger.info(f"✅ Gemini Client initialized (model: {GEMINI_MODEL})")
        else:
            llm_client = LMStudioClient(base_url=INFERENCE_URL, model=MODEL_NAME)
            logger.info(f"✅ LM Client initialized (URL: {INFERENCE_URL})")
    except Exception as e:
        logger.error(f"❌ Failed to initialize LLM Client: {e}")
        raise

@app.get("/health")
async def health_check():
    """Health check endpoint"""
    return {
        "status": "healthy",
        "service": "Bible-Companion",
        "inference_url": INFERENCE_URL,
        "model": MODEL_NAME
    }

@app.post("/api/chat", response_model=ChatResponse)
async def chat_endpoint(
    request: ChatRequest,
    api_key: str = Depends(verify_api_key)
) -> ChatResponse:
    """
    Main chat endpoint for Christological Scripture interpretation.

    Requires: Authorization: Bearer <api_key> header

    Workflow:
    1. Retrieve relevant Scripture passages (semantic search)
    2. Augment system prompt with doctrinal context
    3. Call LFM2.5 inference
    4. Parse response for Scripture references
    5. Enrich response with translations + doctrinal context
    6. Log conversation for finetuning
    """
    if not oracle or not llm_client:
        raise HTTPException(status_code=503, detail="Service not initialized")

    conversation_id = request.conversation_id or f"conv_{datetime.now().isoformat()}"
    user_message = request.message

    try:
        # Step 1: Retrieve relevant Scripture (semantic search)
        logger.info(f"🔍 Searching Oracle for context: '{user_message[:50]}...'")
        relevant_verses = oracle.retrieve_verses(user_message, k=5)

        # Step 2: Build augmented system prompt with doctrinal context
        augmented_prompt = build_augmented_system_prompt(relevant_verses)

        # Step 3: Load conversation history and call Gemini inference
        logger.info("🤖 Calling Gemini inference...")
        history_messages = load_conversation_history(conversation_id, max_turns=MAX_HISTORY_TURNS)
        messages = [
            {"role": "system", "content": augmented_prompt},
            *history_messages,
            {"role": "user", "content": user_message}
        ]
        response_text = await llm_client.generate(
            messages=messages,
            temperature=0.6,
            max_tokens=1024
        )

        # Step 4: Parse response for Scripture references
        scripture_references = extract_scripture_references(response_text)

        # Step 5: Enrich response with Scripture context
        enriched_verses = []
        for ref in scripture_references:
            verses = oracle.retrieve_verses_by_reference(ref.reference)
            if verses:
                enriched_verses.append({
                    "reference": ref.reference,
                    "verses": verses,  # All 5 translations
                    "doctrinal_context": oracle.get_doctrinal_context(ref.reference)
                })

        # Step 6: Log conversation for finetuning
        log_conversation(
            conversation_id,
            user_message,
            response_text,
            scripture_references,
            enriched_verses
        )

        return ChatResponse(
            conversation_id=conversation_id,
            user_message=user_message,
            response=response_text,
            scripture_references=enriched_verses,
            model="Bible-Companion (LFM2.5)",
            timestamp=datetime.now().isoformat()
        )

    except Exception as e:
        logger.error(f"❌ Error in chat endpoint: {e}")
        raise HTTPException(status_code=500, detail=str(e))

def build_augmented_system_prompt(relevant_verses: list[dict]) -> str:
    """
    Augment the base system prompt with retrieved Scripture context.

    Args:
        relevant_verses: List of verse dictionaries from Oracle

    Returns:
        Augmented system prompt with Scripture context
    """
    prompt = SYSTEM_PROMPT

    if relevant_verses:
        prompt += "\n\n## Relevant Scripture Context:\n"
        for verse in relevant_verses[:3]:  # Limit to top 3 for token efficiency
            ref = verse.get('reference', 'Unknown')
            text = verse.get('text', '')
            prompt += f"- {ref}: \"{text}\"\n"

    return prompt

def extract_scripture_references(text: str) -> list[ScriptureReference]:
    """
    Extract Scripture references from response text using regex.
    Pattern: [A-Z0-9 :]+\d+:\d+ (e.g., "John 3:16", "1 Corinthians 13:4-7")

    Args:
        text: Response text from LFM2.5

    Returns:
        List of ScriptureReference objects
    """
    pattern = r'([A-Z0-9\s]+)\s(\d+):(\d+)(?:-(\d+))?'
    matches = re.finditer(pattern, text)

    references = []
    for match in matches:
        book = match.group(1).strip()
        chapter = match.group(2)
        start_verse = match.group(3)
        end_verse = match.group(4)

        ref_str = f"{book} {chapter}:{start_verse}"
        if end_verse:
            ref_str += f"-{end_verse}"

        references.append(ScriptureReference(reference=ref_str))

    return references

def load_conversation_history(conversation_id: str, max_turns: int = 6) -> list[dict]:
    """
    Load recent conversation turns from the JSONL log.

    Args:
        conversation_id: Unique conversation identifier
        max_turns: Number of user/assistant pairs to include

    Returns:
        List of message dicts in OpenAI format
    """
    if not conversation_id or not conversations_log.exists():
        return []

    max_messages = max_turns * 2
    history = deque(maxlen=max_messages)

    try:
        with open(conversations_log, 'r') as f:
            for line in f:
                try:
                    entry = json.loads(line)
                except json.JSONDecodeError:
                    continue

                if entry.get('conversation_id') != conversation_id:
                    continue

                user_msg = entry.get('user_message')
                assistant_msg = entry.get('response')

                if user_msg:
                    history.append({"role": "user", "content": user_msg})
                if assistant_msg:
                    history.append({"role": "assistant", "content": assistant_msg})
    except Exception as e:
        logger.warning(f"⚠️ Failed to load conversation history: {e}")
        return []

    return list(history)

def log_conversation(
    conversation_id: str,
    user_message: str,
    response: str,
    scripture_references: list[ScriptureReference],
    enriched_verses: list[dict]
) -> None:
    """
    Log conversation to JSONL file for finetuning pipeline.

    Args:
        conversation_id: Unique conversation identifier
        user_message: User's input
        response: AI's response
        scripture_references: Extracted Scripture references
        enriched_verses: Enriched Scripture with all translations
    """
    log_entry = {
        "timestamp": datetime.now().isoformat(),
        "conversation_id": conversation_id,
        "user_message": user_message,
        "response": response,
        "scripture_references": [ref.reference for ref in scripture_references],
        "enriched_verses": enriched_verses,
        "model": MODEL_NAME
    }

    with open(conversations_log, 'a') as f:
        f.write(json.dumps(log_entry) + '\n')

    logger.info(f"📝 Logged conversation: {conversation_id}")

# Bible API Endpoints (proxy to api.bible)
API_BIBLE_KEY = os.getenv("API_BIBLE_KEY", "e3cbbfdce355dbc5fefa6c2e2368f116")
API_BIBLE_BASE_URL = "https://api.scripture.api.bible/v1"

@app.get("/api/translations")
async def get_translations():
    """Get list of available Bible translations"""
    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{API_BIBLE_BASE_URL}/bibles",
                headers={"api-key": API_BIBLE_KEY},
                params={"language": "eng"}
            )
            
            if response.status_code == 200:
                data = response.json()
                translations = []
                
                priority_ids = {
                    "de4e12af7f28f599-01": "NKJV",
                    "9879dbb7cfe39e4d-01": "KJV", 
                    "c00c6e04efe4ead3-01": "ESV",
                    "06125adad2d5898a-01": "ASV",
                    "de4e12af7f28f599-02": "NIV",
                    "e5d39c1d526df5d9-01": "NLT",
                    "06125adad2d5898a-02": "NASB",
                    "9879dbb7cfe39e4d-02": "WEB"
                }
                
                for bible in data.get("data", []):
                    # Only include priority translations
                    if bible["id"] in priority_ids:
                        trans_id = priority_ids[bible["id"]]
                        translations.append({
                            "id": trans_id,
                            "name": bible["name"],
                            "verse_count": 31102
                        })
                
                return {"translations": translations, "total": len(translations)}
            else:
                return {
                    "translations": [
                        {"id": "KJV", "name": "King James Version", "verse_count": 31102},
                        {"id": "NKJV", "name": "New King James Version", "verse_count": 31102},
                        {"id": "NIV", "name": "New International Version", "verse_count": 31102},
                        {"id": "ESV", "name": "English Standard Version", "verse_count": 31102},
                        {"id": "NLT", "name": "New Living Translation", "verse_count": 31102},
                        {"id": "NASB", "name": "New American Standard Bible", "verse_count": 31102},
                        {"id": "ASV", "name": "American Standard Version", "verse_count": 31102},
                        {"id": "WEB", "name": "World English Bible", "verse_count": 31102}
                    ],
                    "total": 8
                }
    except Exception as e:
        logger.error(f"Failed to fetch translations: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@app.get("/api/bible/chapters/{chapter_id}")
async def get_chapter(chapter_id: str, translation: str = "KJV"):
    """Get Bible chapter content"""
    try:
        translation_map = {
            "KJV": "9879dbb7cfe39e4d-01",
            "NKJV": "de4e12af7f28f599-01",
            "NIV": "de4e12af7f28f599-02",
            "ESV": "c00c6e04efe4ead3-01",
            "NLT": "e5d39c1d526df5d9-01",
            "NASB": "06125adad2d5898a-02",
            "ASV": "06125adad2d5898a-01",
            "WEB": "9879dbb7cfe39e4d-02"
        }
        
        bible_id = translation_map.get(translation, "9879dbb7cfe39e4d-01")
        
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{API_BIBLE_BASE_URL}/bibles/{bible_id}/chapters/{chapter_id}",
                headers={"api-key": API_BIBLE_KEY},
                params={
                    "content-type": "text",
                    "include-notes": "false",
                    "include-titles": "true", 
                    "include-chapter-numbers": "false",
                    "include-verse-numbers": "true",
                    "include-verse-spans": "false"
                }
            )
            
            if response.status_code == 200:
                data = response.json()
                chapter_data = data.get("data", {})
                return {
                    "data": {
                        "id": chapter_data.get("id", chapter_id),
                        "content": chapter_data.get("content", ""),
                        "translation": translation,
                        "copyright": chapter_data.get("copyright", "")
                    }
                }
            else:
                raise HTTPException(status_code=response.status_code, detail=f"Failed to fetch chapter: {response.text}")
    except Exception as e:
        logger.error(f"Failed to fetch chapter: {e}")
        raise HTTPException(status_code=500, detail=str(e))



@app.get("/api/bibles/{bible_id}/search")
async def search_verses(bible_id: str, query: str, limit: int = 50, offset: int = 0):
    """Search for verses in a specific Bible translation"""
    try:
        # Map short codes to api.bible IDs
        translation_map = {
            "KJV": "9879dbb7cfe39e4d-01",
            "NKJV": "de4e12af7f28f599-01",
            "NIV": "de4e12af7f28f599-02",
            "ESV": "c00c6e04efe4ead3-01",
            "NLT": "e5d39c1d526df5d9-01",
            "NASB": "06125adad2d5898a-02",
            "ASV": "06125adad2d5898a-01",
            "WEB": "9879dbb7cfe39e4d-02",
            "de4e12af7f28f599-01": "de4e12af7f28f599-01",
            "9879dbb7cfe39e4d-01": "9879dbb7cfe39e4d-01",
            "c00c6e04efe4ead3-01": "c00c6e04efe4ead3-01",
            "06125adad2d5898a-01": "06125adad2d5898a-01",
            "de4e12af7f28f599-02": "de4e12af7f28f599-02",
            "e5d39c1d526df5d9-01": "e5d39c1d526df5d9-01",
            "06125adad2d5898a-02": "06125adad2d5898a-02",
            "9879dbb7cfe39e4d-02": "9879dbb7cfe39e4d-02"
        }
        
        api_bible_id = translation_map.get(bible_id, bible_id)
        
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{API_BIBLE_BASE_URL}/bibles/{api_bible_id}/search",
                headers={"api-key": API_BIBLE_KEY},
                params={
                    "query": query,
                    "limit": limit,
                    "offset": offset
                }
            )
            
            if response.status_code == 200:
                data = response.json()
                return data
            else:
                logger.error(f"API.Bible search error: {response.status_code} - {response.text}")
                raise HTTPException(status_code=response.status_code, detail=f"Bible search failed: {response.text}")
    except Exception as e:
        logger.error(f"Failed to search verses: {e}")
        raise HTTPException(status_code=500, detail=str(e))

if __name__ == "__main__":
    import uvicorn

    # Try Auto SSL first, fallback to self-signed
    ssl_cert = "/home/galileetees/ssl/certs/www_apis_pikzulstudios_com_d4e7e_e0c3f_1776135428_e220d84c8fba0b1efc0d9e4251d9664c.crt"
    ssl_key = "/home/galileetees/ssl/keys/d4e7e_e0c3f_1ab605f99121430f3566d212d96760c3.key"

    if not os.path.exists(ssl_cert) or not os.path.exists(ssl_key):
        base_dir = os.path.dirname(os.path.abspath(__file__))
        ssl_cert = os.path.join(base_dir, "server.crt")
        ssl_key = os.path.join(base_dir, "server.key")
        print("[HTTPS] Using self-signed certificates")
    else:
        print("[HTTPS] Using Auto SSL from cPanel")

    uvicorn.run(app, host="0.0.0.0", port=8002, ssl_certfile=ssl_cert, ssl_keyfile=ssl_key, workers=2)
