"""
Authevo Local Action Store

Stores full action data locally in the customer's environment.
Authevo Cloud only receives cryptographic hashes.
"""

import json
import os
import sqlite3
import uuid
from datetime import datetime
from typing import Dict, Any, Optional, List
from pathlib import Path


class LocalActionStore:
    """
    Local storage for full action data.
    
    Authevo Cloud never sees this data - only the SHA256 hash.
    This allows customers to:
    1. Keep sensitive data in their own infrastructure
    2. Provide raw data during disputes for verification
    3. Generate their own audit reports
    
    Example:
        store = LocalActionStore()
        local_id = await store.save(action_data, action_hash)
        
        # Later, for dispute resolution:
        raw_action = await store.get_by_hash(action_hash)
    """
    
    def __init__(self, storage_path: Optional[str] = None):
        """
        Initialize local action store.
        
        Args:
            storage_path: Path to SQLite database. 
                          Defaults to AUTHEVO_LOCAL_STORE env var or ./authevo_actions.db
        """
        self.storage_path = storage_path or os.getenv(
            "AUTHEVO_LOCAL_STORE", 
            str(Path.home() / ".authevo" / "actions.db")
        )
        
        # Ensure directory exists
        Path(self.storage_path).parent.mkdir(parents=True, exist_ok=True)
        
        self._init_db()
    
    def _init_db(self):
        """Initialize SQLite database with required tables."""
        with sqlite3.connect(self.storage_path) as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS local_actions (
                    id TEXT PRIMARY KEY,
                    action_hash TEXT NOT NULL UNIQUE,
                    action_json TEXT NOT NULL,
                    action_type TEXT,
                    agent_did TEXT,
                    cloud_action_id TEXT,
                    transparency_log_idx INTEGER,
                    created_at TEXT NOT NULL
                )
            """)
            conn.execute("CREATE INDEX IF NOT EXISTS idx_hash ON local_actions(action_hash)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_agent ON local_actions(agent_did)")
            conn.execute("CREATE INDEX IF NOT EXISTS idx_created ON local_actions(created_at)")
            conn.commit()
    
    def save(
        self, 
        action: Dict[str, Any], 
        action_hash: str,
        cloud_action_id: Optional[str] = None,
        transparency_log_idx: Optional[int] = None
    ) -> str:
        """
        Save full action data locally.
        
        Args:
            action: Full action object (with payload, policy decision, etc.)
            action_hash: SHA256 hash of canonicalized action
            cloud_action_id: ID returned from Authevo Cloud
            transparency_log_idx: Transparency log index from cloud
            
        Returns:
            Local action ID
        """
        local_id = str(uuid.uuid4())
        
        with sqlite3.connect(self.storage_path) as conn:
            conn.execute(
                """INSERT INTO local_actions 
                   (id, action_hash, action_json, action_type, agent_did, 
                    cloud_action_id, transparency_log_idx, created_at)
                   VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
                (
                    local_id,
                    action_hash,
                    json.dumps(action),
                    action.get("action"),
                    action.get("agent") or action.get("agentDID"),
                    cloud_action_id,
                    transparency_log_idx,
                    datetime.utcnow().isoformat() + "Z"
                )
            )
            conn.commit()
        
        return local_id
    
    def update_cloud_response(
        self, 
        action_hash: str, 
        cloud_action_id: str,
        transparency_log_idx: Optional[int] = None
    ):
        """Update local record with cloud response data."""
        with sqlite3.connect(self.storage_path) as conn:
            conn.execute(
                """UPDATE local_actions 
                   SET cloud_action_id = ?, transparency_log_idx = ?
                   WHERE action_hash = ?""",
                (cloud_action_id, transparency_log_idx, action_hash)
            )
            conn.commit()
    
    def get_by_hash(self, action_hash: str) -> Optional[Dict[str, Any]]:
        """
        Retrieve full action by hash.
        
        Used for dispute resolution - provide raw data to prove hash matches.
        """
        with sqlite3.connect(self.storage_path) as conn:
            conn.row_factory = sqlite3.Row
            cursor = conn.execute(
                "SELECT * FROM local_actions WHERE action_hash = ?",
                (action_hash,)
            )
            row = cursor.fetchone()
            
            if not row:
                return None
            
            return {
                "id": row["id"],
                "action_hash": row["action_hash"],
                "action": json.loads(row["action_json"]),
                "action_type": row["action_type"],
                "agent_did": row["agent_did"],
                "cloud_action_id": row["cloud_action_id"],
                "transparency_log_idx": row["transparency_log_idx"],
                "created_at": row["created_at"]
            }
    
    def get_by_id(self, local_id: str) -> Optional[Dict[str, Any]]:
        """Retrieve action by local ID."""
        with sqlite3.connect(self.storage_path) as conn:
            conn.row_factory = sqlite3.Row
            cursor = conn.execute(
                "SELECT * FROM local_actions WHERE id = ?",
                (local_id,)
            )
            row = cursor.fetchone()
            
            if not row:
                return None
            
            return {
                "id": row["id"],
                "action_hash": row["action_hash"],
                "action": json.loads(row["action_json"]),
                "cloud_action_id": row["cloud_action_id"],
                "created_at": row["created_at"]
            }
    
    def list_actions(
        self, 
        agent_did: Optional[str] = None,
        start_date: Optional[datetime] = None,
        end_date: Optional[datetime] = None,
        limit: int = 100
    ) -> List[Dict[str, Any]]:
        """List actions with optional filtering."""
        query = "SELECT * FROM local_actions WHERE 1=1"
        params: List[Any] = []
        
        if agent_did:
            query += " AND agent_did = ?"
            params.append(agent_did)
        
        if start_date:
            query += " AND created_at >= ?"
            params.append(start_date.isoformat())
        
        if end_date:
            query += " AND created_at <= ?"
            params.append(end_date.isoformat())
        
        query += " ORDER BY created_at DESC LIMIT ?"
        params.append(limit)
        
        with sqlite3.connect(self.storage_path) as conn:
            conn.row_factory = sqlite3.Row
            cursor = conn.execute(query, params)
            rows = cursor.fetchall()
            
            return [
                {
                    "id": row["id"],
                    "action_hash": row["action_hash"],
                    "action": json.loads(row["action_json"]),
                    "action_type": row["action_type"],
                    "cloud_action_id": row["cloud_action_id"],
                    "created_at": row["created_at"]
                }
                for row in rows
            ]
    
    def export_for_audit(
        self, 
        start_date: datetime, 
        end_date: datetime,
        agent_did: Optional[str] = None
    ) -> List[Dict[str, Any]]:
        """
        Export actions for external audit.
        
        Returns full action data that can be verified against Authevo Cloud hashes.
        """
        return self.list_actions(
            agent_did=agent_did,
            start_date=start_date,
            end_date=end_date,
            limit=10000  # Higher limit for audits
        )
    
    def count(self, agent_did: Optional[str] = None) -> int:
        """Count total actions stored locally."""
        with sqlite3.connect(self.storage_path) as conn:
            if agent_did:
                cursor = conn.execute(
                    "SELECT COUNT(*) FROM local_actions WHERE agent_did = ?",
                    (agent_did,)
                )
            else:
                cursor = conn.execute("SELECT COUNT(*) FROM local_actions")
            return cursor.fetchone()[0]
