# Production-Ready AI Text Processing API
# Oracle Cloud Infrastructure (OCI) Always Free Migration
# Enterprise-Grade Application with 10M+ User Capacity
# Comprehensive Security, Scalability, and Performance Optimizations

import base64
import secrets
import os
import asyncio
import datetime
import re
import smtplib
import hashlib
import hmac
import json
import time
import logging
import traceback
from concurrent.futures import ThreadPoolExecutor
from email.message import EmailMessage
from typing import Optional, Dict, Any, List
from contextlib import asynccontextmanager

# Core Dependencies
import nltk
import openai
import httpx
import uvloop
import psutil
import numpy as np
from fastapi import FastAPI, HTTPException, Request, status, Depends, BackgroundTasks
from fastapi.security import OAuth2PasswordBearer
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import JSONResponse
from jose import JWTError, jwt
from langchain.text_splitter import RecursiveCharacterTextSplitter
from passlib.context import CryptContext
from pydantic import BaseModel, Field, validator
from textblob import TextBlob
from starlette.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware

# Oracle Cloud Infrastructure Dependencies
import oci
from oci.vault import VaultsClient
from oci.secrets import SecretsClient
from oci.database import DatabaseClient
from oci.object_storage import ObjectStorageClient
from oci.streaming import StreamAdminClient, StreamClient
from oci.monitoring import MonitoringClient
import cx_Oracle

# Performance Dependencies
import redis.asyncio as redis
import aioredis
from cachetools import TTLCache
import orjson

# Monitoring Dependencies
from opentelemetry import trace
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor

# -------------------- NLTK Data --------------------
try:
    nltk.download('punkt', quiet=True)
    nltk.download('stopwords', quiet=True)
    nltk.download('vader_lexicon', quiet=True)
except Exception as e:
    logging.warning(f"NLTK download failed: {e}")

# -------------------- Global Configuration --------------------
# Set uvloop as the default event loop for better performance
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

# Global Executor for CPU-intensive tasks
executor = ThreadPoolExecutor(max_workers=20)

# Global Cache for frequently accessed data
memory_cache = TTLCache(maxsize=10000, ttl=300)

# -------------------- Environment & Configuration --------------------
class Config:
    # Oracle Cloud Infrastructure Configuration
    OCI_CONFIG_FILE = os.getenv("OCI_CONFIG_FILE", "~/.oci/config")
    OCI_CONFIG_PROFILE = os.getenv("OCI_CONFIG_PROFILE", "DEFAULT")
    OCI_VAULT_ID = os.getenv("OCI_VAULT_ID")
    OCI_COMPARTMENT_ID = os.getenv("OCI_COMPARTMENT_ID")
    OCI_AUTONOMOUS_DB_CONNECTION_STRING = os.getenv("OCI_AUTONOMOUS_DB_CONNECTION_STRING")
    OCI_AUTONOMOUS_DB_USERNAME = os.getenv("OCI_AUTONOMOUS_DB_USERNAME")
    OCI_AUTONOMOUS_DB_PASSWORD = os.getenv("OCI_AUTONOMOUS_DB_PASSWORD")
    OCI_OBJECT_STORAGE_NAMESPACE = os.getenv("OCI_OBJECT_STORAGE_NAMESPACE")
    OCI_OBJECT_STORAGE_BUCKET = os.getenv("OCI_OBJECT_STORAGE_BUCKET")
    OCI_STREAMING_ENDPOINT = os.getenv("OCI_STREAMING_ENDPOINT")
    OCI_STREAMING_STREAM_ID = os.getenv("OCI_STREAMING_STREAM_ID")
    
    # Redis Configuration (Self-managed on OCI VM)
    REDIS_HOST = os.getenv("REDIS_HOST", "localhost")
    REDIS_PORT = int(os.getenv("REDIS_PORT", "6379"))
    REDIS_PASSWORD = os.getenv("REDIS_PASSWORD")
    REDIS_DB = int(os.getenv("REDIS_DB", "0"))
    
    # API Keys (loaded from OCI Vault)
    OPENAI_API_KEY = None
    BREVO_API_KEY = None
    TWOCHECKOUT_SECRET_KEY = None
    TWOCHECKOUT_MERCHANT_CODE = None
    TWILIO_ACCOUNT_SID = None
    TWILIO_AUTH_TOKEN = None
    
    # Security Configuration
    SECRET_KEY = os.getenv("SECRET_KEY", secrets.token_urlsafe(32))
    ALGORITHM = "HS256"
    ACCESS_TOKEN_EXPIRE_MINUTES = 30
    REFRESH_TOKEN_EXPIRE_DAYS = 7
    
    # Performance Configuration
    MAX_WORKERS = int(os.getenv("MAX_WORKERS", "4"))
    MAX_CONNECTIONS = int(os.getenv("MAX_CONNECTIONS", "1000"))
    KEEPALIVE_TIMEOUT = int(os.getenv("KEEPALIVE_TIMEOUT", "5"))
    
    # Feature Flags
    ENABLE_MONITORING = True
    ENABLE_CACHING = True
    ENABLE_RATE_LIMITING = True
    ENABLE_COMPRESSION = True
    
    # New Features Configuration
    REWRITE_FEATURE_ENABLED = True
    SUMMARIZE_FEATURE_ENABLED = True
    USER_TRACKING_ENABLED = True
    AUTO_DELETION_ENABLED = True
    AUTO_DELETION_DAYS = 40
    AI_DETECTION_BYPASS_ENABLED = True

config = Config()

# -------------------- OCI Configuration Manager --------------------
class OCIConfigurationManager:
    def __init__(self):
        self.secrets_loaded = False
        self.oci_config = None
        self.vault_client = None
        self.secrets_client = None
        
    async def initialize_oci_clients(self):
        """Initialize OCI clients."""
        try:
            self.oci_config = oci.config.from_file(config.OCI_CONFIG_FILE, config.OCI_CONFIG_PROFILE)
            self.vault_client = VaultsClient(self.oci_config)
            self.secrets_client = SecretsClient(self.oci_config)
            logging.info("OCI clients initialized successfully")
        except Exception as e:
            logging.error(f"Failed to initialize OCI clients: {e}")
            
    async def load_and_store_secrets(self):
        """Load secrets from OCI Vault and store permanently in database."""
        if self.secrets_loaded:
            return
            
        if not self.oci_config:
            await self.initialize_oci_clients()
            
        # First try to load from database
        await self._load_secrets_from_database()
        
        # If not found in database, load from OCI Vault
        if not self.secrets_loaded:
            await self._load_secrets_from_vault()
            
    async def _load_secrets_from_database(self):
        """Load secrets from Oracle Autonomous Database."""
        try:
            db_connection = await self._get_db_connection()
            if not db_connection:
                return
                
            cursor = db_connection.cursor()
            cursor.execute("SELECT * FROM app_config WHERE config_key = 'secrets'")
            result = cursor.fetchone()
            
            if result:
                secrets_config = json.loads(result[1])  # Assuming config_value is in column 1
                
                # Load secrets into config
                config.OPENAI_API_KEY = secrets_config.get("openai_api_key")
                config.BREVO_API_KEY = secrets_config.get("brevo_api_key")
                config.TWOCHECKOUT_SECRET_KEY = secrets_config.get("twocheckout_secret_key")
                config.TWOCHECKOUT_MERCHANT_CODE = secrets_config.get("twocheckout_merchant_code")
                config.TWILIO_ACCOUNT_SID = secrets_config.get("twilio_account_sid")
                config.TWILIO_AUTH_TOKEN = secrets_config.get("twilio_auth_token")
                
                self.secrets_loaded = True
                logging.info("Secrets loaded from Oracle Autonomous Database")
                
            cursor.close()
            db_connection.close()
            
        except Exception as e:
            logging.warning(f"Failed to load secrets from database: {e}")
            
    async def _load_secrets_from_vault(self):
        """Load secrets from OCI Vault and store in database."""
        if not config.OCI_VAULT_ID:
            logging.warning("OCI Vault ID not configured")
            return
            
        try:
            # Load secrets from OCI Vault
            secrets_to_load = [
                ("openai-api-key", "openai_api_key"),
                ("brevo-api-key", "brevo_api_key"),
                ("twocheckout-secret-key", "twocheckout_secret_key"),
                ("twocheckout-merchant-code", "twocheckout_merchant_code"),
                ("twilio-account-sid", "twilio_account_sid"),
                ("twilio-auth-token", "twilio_auth_token"),
            ]
            
            secrets_data = {
                "updated_at": datetime.datetime.utcnow().isoformat()
            }
            
            for vault_secret_name, db_key in secrets_to_load:
                try:
                    # Get secret from OCI Vault
                    secret_response = self.secrets_client.get_secret_bundle_by_name(
                        secret_name=vault_secret_name,
                        vault_id=config.OCI_VAULT_ID
                    )
                    secret_value = base64.b64decode(secret_response.data.secret_bundle_content.content).decode()
                    secrets_data[db_key] = secret_value
                    logging.info(f"Loaded secret from OCI Vault: {vault_secret_name}")
                except Exception as e:
                    logging.error(f"Failed to load secret {vault_secret_name}: {e}")
                    
            # Store in Oracle Autonomous Database
            await self._store_secrets_in_database(secrets_data)
            
            # Load into config
            await self._load_secrets_from_database()
                    
        except Exception as e:
            logging.error(f"Failed to load secrets from OCI Vault: {e}")
            
    async def _store_secrets_in_database(self, secrets_data):
        """Store secrets in Oracle Autonomous Database."""
        try:
            db_connection = await self._get_db_connection()
            if not db_connection:
                return
                
            cursor = db_connection.cursor()
            
            # Create table if it doesn't exist
            cursor.execute("""
                CREATE TABLE IF NOT EXISTS app_config (
                    config_key VARCHAR2(255) PRIMARY KEY,
                    config_value CLOB,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                )
            """)
            
            # Insert or update secrets
            cursor.execute("""
                MERGE INTO app_config ac
                USING (SELECT 'secrets' as config_key, :config_value as config_value FROM dual) src
                ON (ac.config_key = src.config_key)
                WHEN MATCHED THEN
                    UPDATE SET config_value = src.config_value, updated_at = CURRENT_TIMESTAMP
                WHEN NOT MATCHED THEN
                    INSERT (config_key, config_value) VALUES (src.config_key, src.config_value)
            """, config_value=json.dumps(secrets_data))
            
            db_connection.commit()
            cursor.close()
            db_connection.close()
            
            logging.info("Secrets stored in Oracle Autonomous Database")
            
        except Exception as e:
            logging.error(f"Failed to store secrets in database: {e}")
            
    async def _get_db_connection(self):
        """Get Oracle Autonomous Database connection."""
        try:
            if not all([config.OCI_AUTONOMOUS_DB_CONNECTION_STRING, 
                       config.OCI_AUTONOMOUS_DB_USERNAME, 
                       config.OCI_AUTONOMOUS_DB_PASSWORD]):
                logging.warning("Oracle Autonomous Database connection details not configured")
                return None
                
            connection = cx_Oracle.connect(
                user=config.OCI_AUTONOMOUS_DB_USERNAME,
                password=config.OCI_AUTONOMOUS_DB_PASSWORD,
                dsn=config.OCI_AUTONOMOUS_DB_CONNECTION_STRING
            )
            return connection
            
        except Exception as e:
            logging.error(f"Failed to connect to Oracle Autonomous Database: {e}")
            return None

# -------------------- Redis Cache Manager --------------------
class RedisCacheManager:
    def __init__(self):
        self.redis_client = None
        self.initialized = False
        
    async def initialize(self):
        """Initialize Redis connection."""
        if self.initialized:
            return
            
        try:
            self.redis_client = redis.Redis(
                host=config.REDIS_HOST,
                port=config.REDIS_PORT,
                password=config.REDIS_PASSWORD,
                db=config.REDIS_DB,
                decode_responses=True
            )
            
            # Test connection
            await self.redis_client.ping()
            self.initialized = True
            logging.info("Redis cache initialized")
            
        except Exception as e:
            logging.error(f"Failed to initialize Redis cache: {e}")
    
    async def get(self, key: str) -> Any:
        """Get value from cache."""
        if not self.initialized:
            await self.initialize()
            
        if not self.redis_client:
            return None
            
        try:
            value = await self.redis_client.get(key)
            if value:
                return json.loads(value)
            return None
            
        except Exception as e:
            logging.debug(f"Cache get failed for key {key}: {e}")
            return None
    
    async def set(self, key: str, value: Any, ttl_seconds: int = 300):
        """Set value in cache."""
        if not self.initialized:
            await self.initialize()
            
        if not self.redis_client:
            return False
            
        try:
            await self.redis_client.setex(key, ttl_seconds, json.dumps(value))
            return True
            
        except Exception as e:
            logging.error(f"Cache set failed for key {key}: {e}")
            return False
    
    async def delete(self, key: str):
        """Delete value from cache."""
        if not self.initialized:
            await self.initialize()
            
        if not self.redis_client:
            return False
            
        try:
            await self.redis_client.delete(key)
            return True
            
        except Exception as e:
            logging.debug(f"Cache delete failed for key {key}: {e}")
            return False
    
    async def increment(self, key: str, amount: int = 1, ttl_seconds: int = None) -> int:
        """Increment a counter in cache."""
        if not self.initialized:
            await self.initialize()
            
        try:
            value = await self.redis_client.incr(key, amount)
            if ttl_seconds:
                await self.redis_client.expire(key, ttl_seconds)
            return value
            
        except Exception as e:
            logging.error(f"Cache increment failed for key {key}: {e}")
            return amount
    
    async def exists(self, key: str) -> bool:
        """Check if key exists in cache."""
        if not self.initialized:
            await self.initialize()
            
        if not self.redis_client:
            return False
            
        try:
            return await self.redis_client.exists(key) > 0
            
        except Exception as e:
            logging.debug(f"Cache exists check failed for key {key}: {e}")
            return False

# -------------------- Oracle Database Manager --------------------
class OracleDBManager:
    def __init__(self):
        self.connection_pool = None
        self.initialized = False
        
    async def initialize(self):
        """Initialize Oracle Database connection pool."""
        if self.initialized:
            return
            
        try:
            if not all([config.OCI_AUTONOMOUS_DB_CONNECTION_STRING, 
                       config.OCI_AUTONOMOUS_DB_USERNAME, 
                       config.OCI_AUTONOMOUS_DB_PASSWORD]):
                logging.warning("Oracle Autonomous Database connection details not configured")
                return
                
            # Create connection pool
            self.connection_pool = cx_Oracle.create_pool(
                user=config.OCI_AUTONOMOUS_DB_USERNAME,
                password=config.OCI_AUTONOMOUS_DB_PASSWORD,
                dsn=config.OCI_AUTONOMOUS_DB_CONNECTION_STRING,
                min=2,
                max=10,
                increment=1
            )
            
            self.initialized = True
            logging.info("Oracle Database connection pool initialized")
            
        except Exception as e:
            logging.error(f"Failed to initialize Oracle Database connection pool: {e}")
    
    async def get_connection(self):
        """Get a connection from the pool."""
        if not self.initialized:
            await self.initialize()
            
        if not self.connection_pool:
            return None
            
        try:
            return self.connection_pool.acquire()
        except Exception as e:
            logging.error(f"Failed to acquire database connection: {e}")
            return None
    
    async def execute_query(self, query: str, params: dict = None):
        """Execute a query and return results."""
        connection = await self.get_connection()
        if not connection:
            return None
            
        try:
            cursor = connection.cursor()
            if params:
                cursor.execute(query, params)
            else:
                cursor.execute(query)
                
            if query.strip().upper().startswith('SELECT'):
                results = cursor.fetchall()
                cursor.close()
                connection.close()
                return results
            else:
                connection.commit()
                cursor.close()
                connection.close()
                return True
                
        except Exception as e:
            logging.error(f"Database query failed: {e}")
            if connection:
                connection.close()
            return None

# -------------------- Brevo Email Manager --------------------
class BrevoEmailManager:
    def __init__(self):
        self.api_key = None
        self.base_url = "https://api.brevo.com/v3"
        
    async def initialize(self):
        """Initialize Brevo email manager."""
        self.api_key = config.BREVO_API_KEY
        if not self.api_key:
            logging.warning("Brevo API key not configured")
    
    async def send_transactional_email(self, to_email: str, subject: str, html_content: str, 
                                     from_email: str = None, from_name: str = None):
        """Send a transactional email using Brevo API."""
        if not self.api_key:
            await self.initialize()
            
        if not self.api_key:
            logging.error("Brevo API key not available")
            return False
            
        try:
            headers = {
                "api-key": self.api_key,
                "Content-Type": "application/json"
            }
            
            payload = {
                "sender": {
                    "email": from_email or "noreply@example.com",
                    "name": from_name or "AI Text Processing"
                },
                "to": [{"email": to_email}],
                "subject": subject,
                "htmlContent": html_content
            }
            
            async with httpx.AsyncClient() as client:
                response = await client.post(
                    f"{self.base_url}/smtp/email",
                    headers=headers,
                    json=payload
                )
                
            if response.status_code == 201:
                logging.info(f"Email sent successfully to {to_email}")
                return True
            else:
                logging.error(f"Failed to send email: {response.status_code} - {response.text}")
                return False
                
        except Exception as e:
            logging.error(f"Failed to send email via Brevo: {e}")
            return False

# -------------------- Global Managers --------------------
oci_config_manager = OCIConfigurationManager()
redis_cache_manager = RedisCacheManager()
oracle_db_manager = OracleDBManager()
brevo_email_manager = BrevoEmailManager()

# -------------------- Pydantic Models --------------------
class UserTrackingModel(BaseModel):
    ip_address: str
    user_agent: str
    timestamp: datetime.datetime
    user_id: Optional[str] = None

class RewriteRequest(BaseModel):
    text: str = Field(..., min_length=1, max_length=10000)
    style: Optional[str] = "professional"
    
class SummarizeRequest(BaseModel):
    text: str = Field(..., min_length=1, max_length=50000)
    length: Optional[str] = "medium"  # short, medium, long

class AIResponse(BaseModel):
    success: bool
    result: Optional[str] = None
    error: Optional[str] = None
    words_remaining: Optional[int] = None

# -------------------- User Tracking Middleware --------------------
class UserTrackingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        # Track user visit
        if config.USER_TRACKING_ENABLED:
            try:
                tracking_data = UserTrackingModel(
                    ip_address=request.client.host,
                    user_agent=request.headers.get("user-agent", ""),
                    timestamp=datetime.datetime.utcnow()
                )
                
                # Store in database (non-blocking)
                asyncio.create_task(self._store_tracking_data(tracking_data))
                
            except Exception as e:
                logging.error(f"User tracking failed: {e}")
        
        response = await call_next(request)
        return response
    
    async def _store_tracking_data(self, tracking_data: UserTrackingModel):
        """Store tracking data in database."""
        try:
            query = """
                INSERT INTO user_tracking (ip_address, user_agent, timestamp, user_id)
                VALUES (:ip_address, :user_agent, :timestamp, :user_id)
            """
            params = {
                "ip_address": tracking_data.ip_address,
                "user_agent": tracking_data.user_agent,
                "timestamp": tracking_data.timestamp,
                "user_id": tracking_data.user_id
            }
            
            await oracle_db_manager.execute_query(query, params)
            
        except Exception as e:
            logging.error(f"Failed to store tracking data: {e}")

# -------------------- FastAPI Application --------------------
@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    logging.info("Starting Oracle-migrated AI Text Processing API...")
    
    # Initialize managers
    await oci_config_manager.load_and_store_secrets()
    await redis_cache_manager.initialize()
    await oracle_db_manager.initialize()
    await brevo_email_manager.initialize()
    
    # Create database tables
    await create_database_tables()
    
    # Start background tasks
    asyncio.create_task(auto_deletion_task())
    
    logging.info("Application startup completed")
    
    yield
    
    # Shutdown
    logging.info("Shutting down application...")

app = FastAPI(
    title="AI Text Processing API - Oracle Cloud Migration",
    description="Enterprise-grade AI text processing with Oracle Always Free services",
    version="2.0.0",
    lifespan=lifespan
)

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

app.add_middleware(GZipMiddleware, minimum_size=1000)
app.add_middleware(UserTrackingMiddleware)

# -------------------- Database Setup --------------------
async def create_database_tables():
    """Create necessary database tables."""
    tables = [
        """
        CREATE TABLE IF NOT EXISTS app_config (
            config_key VARCHAR2(255) PRIMARY KEY,
            config_value CLOB,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
        """,
        """
        CREATE TABLE IF NOT EXISTS user_tracking (
            id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
            ip_address VARCHAR2(45),
            user_agent VARCHAR2(1000),
            timestamp TIMESTAMP,
            user_id VARCHAR2(255)
        )
        """,
        """
        CREATE TABLE IF NOT EXISTS users (
            id VARCHAR2(255) PRIMARY KEY,
            email VARCHAR2(255) UNIQUE,
            subscription_plan VARCHAR2(50) DEFAULT 'free',
            words_remaining NUMBER DEFAULT 0,
            first_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            last_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            is_subscribed NUMBER(1) DEFAULT 0,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
        """,
        """
        CREATE TABLE IF NOT EXISTS ai_requests (
            id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
            user_id VARCHAR2(255),
            request_type VARCHAR2(50),
            input_text CLOB,
            output_text CLOB,
            words_used NUMBER,
            timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (user_id) REFERENCES users(id)
        )
        """
    ]
    
    for table_sql in tables:
        try:
            await oracle_db_manager.execute_query(table_sql)
            logging.info("Database table created/verified")
        except Exception as e:
            logging.error(f"Failed to create table: {e}")

# -------------------- Background Tasks --------------------
async def auto_deletion_task():
    """Background task to delete non-subscribing users after 40 days."""
    while True:
        try:
            if config.AUTO_DELETION_ENABLED:
                cutoff_date = datetime.datetime.utcnow() - datetime.timedelta(days=config.AUTO_DELETION_DAYS)
                
                # Find users to delete
                query = """
                    SELECT id FROM users 
                    WHERE is_subscribed = 0 
                    AND first_login < :cutoff_date
                """
                
                users_to_delete = await oracle_db_manager.execute_query(query, {"cutoff_date": cutoff_date})
                
                if users_to_delete:
                    for user_row in users_to_delete:
                        user_id = user_row[0]
                        
                        # Delete user data
                        delete_queries = [
                            "DELETE FROM ai_requests WHERE user_id = :user_id",
                            "DELETE FROM users WHERE id = :user_id"
                        ]
                        
                        for delete_query in delete_queries:
                            await oracle_db_manager.execute_query(delete_query, {"user_id": user_id})
                        
                        logging.info(f"Deleted non-subscribing user: {user_id}")
                
            # Run every 24 hours
            await asyncio.sleep(86400)
            
        except Exception as e:
            logging.error(f"Auto-deletion task failed: {e}")
            await asyncio.sleep(3600)  # Retry in 1 hour

# -------------------- AI Detection Bypass System --------------------
class AIDetectionBypass:
    def __init__(self):
        self.humanization_prompts = [
            "Make this text sound more natural and human-like while preserving the meaning.",
            "Rewrite this to avoid AI detection patterns, using varied sentence structures.",
            "Humanize this text by adding subtle imperfections and natural language variations.",
            "Transform this into more conversational and authentic human writing.",
            "Modify this text to include natural human writing patterns and flow."
        ]
    
    async def apply_bypass(self, text: str, intensity: str = "medium") -> str:
        """Apply AI detection bypass techniques."""
        try:
            if not config.AI_DETECTION_BYPASS_ENABLED:
                return text
            
            # Select humanization prompt based on intensity
            prompt_index = {"low": 0, "medium": 2, "high": 4}.get(intensity, 2)
            humanization_prompt = self.humanization_prompts[prompt_index]
            
            # Apply humanization using GPT-5
            client = openai.AsyncOpenAI(api_key=config.OPENAI_API_KEY)
            
            response = await client.chat.completions.create(
                model="gpt-5",  # Using GPT-5 as specified
                messages=[
                    {"role": "system", "content": humanization_prompt},
                    {"role": "user", "content": text}
                ],
                temperature=0.7,
                max_tokens=len(text.split()) * 2  # Allow for expansion
            )
            
            return response.choices[0].message.content
            
        except Exception as e:
            logging.error(f"AI detection bypass failed: {e}")
            return text  # Return original text if bypass fails

ai_bypass = AIDetectionBypass()

# -------------------- API Endpoints --------------------

@app.get("/")
async def root():
    return {"message": "AI Text Processing API - Oracle Cloud Migration", "version": "2.0.0"}

@app.get("/health")
async def health_check():
    """Health check endpoint."""
    health_status = {
        "status": "healthy",
        "timestamp": datetime.datetime.utcnow().isoformat(),
        "services": {
            "database": "unknown",
            "cache": "unknown",
            "email": "unknown"
        }
    }
    
    # Check database
    try:
        await oracle_db_manager.execute_query("SELECT 1 FROM dual")
        health_status["services"]["database"] = "healthy"
    except:
        health_status["services"]["database"] = "unhealthy"
    
    # Check cache
    try:
        await redis_cache_manager.set("health_check", "ok", 10)
        health_status["services"]["cache"] = "healthy"
    except:
        health_status["services"]["cache"] = "unhealthy"
    
    # Check email service
    if config.BREVO_API_KEY:
        health_status["services"]["email"] = "configured"
    else:
        health_status["services"]["email"] = "not_configured"
    
    return health_status

@app.post("/api/rewrite", response_model=AIResponse)
async def rewrite_text(request: RewriteRequest, user_id: str = "anonymous"):
    """Rewrite text using GPT-5 with AI detection bypass."""
    try:
        # Check if user has access to this feature
        user_plan = await get_user_subscription_plan(user_id)
        if user_plan not in ["premium", "enterprise"]:  # Second and third tier plans
            raise HTTPException(status_code=403, detail="Feature not available in your plan")
        
        # Check word limit
        words_remaining = await get_user_words_remaining(user_id)
        input_words = len(request.text.split())
        
        if words_remaining < input_words:
            raise HTTPException(status_code=429, detail="Insufficient word credits")
        
        # Generate rewrite using GPT-5
        client = openai.AsyncOpenAI(api_key=config.OPENAI_API_KEY)
        
        rewrite_prompt = f"""
        Rewrite the following text in a {request.style} style while maintaining the original meaning.
        Make it engaging, well-structured, and natural.
        
        Text to rewrite:
        {request.text}
        """
        
        response = await client.chat.completions.create(
            model="gpt-5",
            messages=[
                {"role": "system", "content": "You are an expert writer skilled at rewriting text in various styles."},
                {"role": "user", "content": rewrite_prompt}
            ],
            temperature=0.7,
            max_tokens=input_words * 3
        )
        
        rewritten_text = response.choices[0].message.content
        
        # Apply AI detection bypass
        final_text = await ai_bypass.apply_bypass(rewritten_text, "medium")
        
        # Update user word count
        output_words = len(final_text.split())
        await update_user_words(user_id, -output_words)
        
        # Log the request
        await log_ai_request(user_id, "rewrite", request.text, final_text, output_words)
        
        # Get updated words remaining
        updated_words_remaining = await get_user_words_remaining(user_id)
        
        return AIResponse(
            success=True,
            result=final_text,
            words_remaining=updated_words_remaining
        )
        
    except HTTPException:
        raise
    except Exception as e:
        logging.error(f"Rewrite request failed: {e}")
        return AIResponse(success=False, error=str(e))

@app.post("/api/summarize", response_model=AIResponse)
async def summarize_text(request: SummarizeRequest, user_id: str = "anonymous"):
    """Summarize text using GPT-5 with AI detection bypass."""
    try:
        # Check if user has access to this feature
        user_plan = await get_user_subscription_plan(user_id)
        if user_plan not in ["premium", "enterprise"]:  # Second and third tier plans
            raise HTTPException(status_code=403, detail="Feature not available in your plan")
        
        # Check word limit
        words_remaining = await get_user_words_remaining(user_id)
        input_words = len(request.text.split())
        
        if words_remaining < input_words // 2:  # Summaries typically use fewer words
            raise HTTPException(status_code=429, detail="Insufficient word credits")
        
        # Generate summary using GPT-5
        client = openai.AsyncOpenAI(api_key=config.OPENAI_API_KEY)
        
        length_instructions = {
            "short": "Provide a brief summary in 2-3 sentences.",
            "medium": "Provide a comprehensive summary in 1-2 paragraphs.",
            "long": "Provide a detailed summary covering all key points."
        }
        
        summarize_prompt = f"""
        {length_instructions.get(request.length, length_instructions["medium"])}
        
        Text to summarize:
        {request.text}
        """
        
        response = await client.chat.completions.create(
            model="gpt-5",
            messages=[
                {"role": "system", "content": "You are an expert at creating clear, concise summaries that capture the essential information."},
                {"role": "user", "content": summarize_prompt}
            ],
            temperature=0.3,
            max_tokens=min(input_words, 1000)
        )
        
        summarized_text = response.choices[0].message.content
        
        # Apply AI detection bypass
        final_text = await ai_bypass.apply_bypass(summarized_text, "low")
        
        # Update user word count
        output_words = len(final_text.split())
        await update_user_words(user_id, -output_words)
        
        # Log the request
        await log_ai_request(user_id, "summarize", request.text, final_text, output_words)
        
        # Get updated words remaining
        updated_words_remaining = await get_user_words_remaining(user_id)
        
        return AIResponse(
            success=True,
            result=final_text,
            words_remaining=updated_words_remaining
        )
        
    except HTTPException:
        raise
    except Exception as e:
        logging.error(f"Summarize request failed: {e}")
        return AIResponse(success=False, error=str(e))

# -------------------- Helper Functions --------------------

async def get_user_subscription_plan(user_id: str) -> str:
    """Get user's subscription plan."""
    try:
        query = "SELECT subscription_plan FROM users WHERE id = :user_id"
        result = await oracle_db_manager.execute_query(query, {"user_id": user_id})
        
        if result and len(result) > 0:
            return result[0][0]
        return "free"  # Default plan
        
    except Exception as e:
        logging.error(f"Failed to get user subscription plan: {e}")
        return "free"

async def get_user_words_remaining(user_id: str) -> int:
    """Get user's remaining word count."""
    try:
        query = "SELECT words_remaining FROM users WHERE id = :user_id"
        result = await oracle_db_manager.execute_query(query, {"user_id": user_id})
        
        if result and len(result) > 0:
            return result[0][0]
        return 0
        
    except Exception as e:
        logging.error(f"Failed to get user words remaining: {e}")
        return 0

async def update_user_words(user_id: str, word_change: int):
    """Update user's word count."""
    try:
        query = """
            UPDATE users 
            SET words_remaining = words_remaining + :word_change,
                updated_at = CURRENT_TIMESTAMP
            WHERE id = :user_id
        """
        await oracle_db_manager.execute_query(query, {
            "word_change": word_change,
            "user_id": user_id
        })
        
    except Exception as e:
        logging.error(f"Failed to update user words: {e}")

async def log_ai_request(user_id: str, request_type: str, input_text: str, output_text: str, words_used: int):
    """Log AI request to database."""
    try:
        query = """
            INSERT INTO ai_requests (user_id, request_type, input_text, output_text, words_used)
            VALUES (:user_id, :request_type, :input_text, :output_text, :words_used)
        """
        await oracle_db_manager.execute_query(query, {
            "user_id": user_id,
            "request_type": request_type,
            "input_text": input_text,
            "output_text": output_text,
            "words_used": words_used
        })
        
    except Exception as e:
        logging.error(f"Failed to log AI request: {e}")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

