"""
AIPTX Beast Mode - Credential Store
===================================

Secure storage and management of harvested credentials.
"""

from __future__ import annotations

import base64
import hashlib
import json
import logging
import sqlite3
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Optional

from aipt_v2.post_exploit.credential_harvester import (
    Credential,
    CredentialType,
    CredentialSource,
)

logger = logging.getLogger(__name__)


@dataclass
class StoredCredential:
    """A credential stored in the database."""
    id: str
    credential_type: CredentialType
    source: CredentialSource
    username: str | None
    password_encrypted: str | None  # Base64 encoded
    domain: str | None
    host: str | None
    port: int | None
    service: str | None
    metadata: dict[str, Any]
    discovered_at: str
    target_engagement: str | None
    verified: bool
    last_used: str | None
    notes: str

    def decrypt_password(self, key: bytes | None = None) -> str | None:
        """Decrypt the stored password."""
        if not self.password_encrypted:
            return None
        # Simple base64 for now - in production, use proper encryption
        try:
            return base64.b64decode(self.password_encrypted).decode()
        except Exception:
            return self.password_encrypted

    def to_credential(self) -> Credential:
        """Convert back to Credential object."""
        return Credential(
            credential_type=self.credential_type,
            source=self.source,
            username=self.username,
            password=self.decrypt_password(),
            domain=self.domain,
            host=self.host,
            port=self.port,
            service=self.service,
            metadata=self.metadata,
            discovered_at=self.discovered_at,
        )

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary (password masked)."""
        return {
            "id": self.id,
            "credential_type": self.credential_type.value,
            "source": self.source.value,
            "username": self.username,
            "password": "********" if self.password_encrypted else None,
            "domain": self.domain,
            "host": self.host,
            "port": self.port,
            "service": self.service,
            "metadata": self.metadata,
            "discovered_at": self.discovered_at,
            "verified": self.verified,
            "notes": self.notes,
        }


class CredentialStore:
    """
    Secure storage for harvested credentials.

    Provides CRUD operations, search, and export functionality.
    """

    def __init__(self, db_path: str | Path | None = None):
        """
        Initialize the credential store.

        Args:
            db_path: Path to SQLite database. Uses default if None.
        """
        if db_path:
            self.db_path = Path(db_path)
        else:
            self.db_path = Path.home() / ".aiptx" / "credentials.db"

        self.db_path.parent.mkdir(parents=True, exist_ok=True)
        self._conn = sqlite3.connect(str(self.db_path), check_same_thread=False)
        self._init_schema()

    def _init_schema(self):
        """Initialize database schema."""
        cursor = self._conn.cursor()

        cursor.execute("""
            CREATE TABLE IF NOT EXISTS credentials (
                id TEXT PRIMARY KEY,
                credential_type TEXT NOT NULL,
                source TEXT NOT NULL,
                username TEXT,
                password_encrypted TEXT,
                domain TEXT,
                host TEXT,
                port INTEGER,
                service TEXT,
                metadata TEXT DEFAULT '{}',
                discovered_at TEXT NOT NULL,
                target_engagement TEXT,
                verified INTEGER DEFAULT 0,
                last_used TEXT,
                notes TEXT DEFAULT '',
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP
            )
        """)

        cursor.execute("""
            CREATE INDEX IF NOT EXISTS idx_creds_type
            ON credentials(credential_type)
        """)

        cursor.execute("""
            CREATE INDEX IF NOT EXISTS idx_creds_host
            ON credentials(host)
        """)

        cursor.execute("""
            CREATE INDEX IF NOT EXISTS idx_creds_service
            ON credentials(service)
        """)

        cursor.execute("""
            CREATE INDEX IF NOT EXISTS idx_creds_engagement
            ON credentials(target_engagement)
        """)

        self._conn.commit()

    def store(
        self,
        credential: Credential,
        engagement: str | None = None,
        notes: str = "",
    ) -> str:
        """
        Store a credential.

        Args:
            credential: The credential to store
            engagement: Optional engagement/target identifier
            notes: Optional notes

        Returns:
            The credential ID
        """
        cursor = self._conn.cursor()

        # Generate ID from credential content
        id_source = f"{credential.username}:{credential.password}:{credential.host}:{credential.service}"
        cred_id = hashlib.sha256(id_source.encode()).hexdigest()[:16]

        # Encrypt password (simple base64 for now)
        password_encrypted = None
        if credential.password:
            password_encrypted = base64.b64encode(credential.password.encode()).decode()

        # Check if exists
        cursor.execute("SELECT id FROM credentials WHERE id = ?", (cred_id,))
        if cursor.fetchone():
            # Update existing
            cursor.execute("""
                UPDATE credentials SET
                    verified = verified,
                    last_used = ?,
                    notes = CASE WHEN ? != '' THEN ? ELSE notes END
                WHERE id = ?
            """, (
                datetime.now(timezone.utc).isoformat(),
                notes,
                notes,
                cred_id,
            ))
        else:
            # Insert new
            cursor.execute("""
                INSERT INTO credentials (
                    id, credential_type, source, username, password_encrypted,
                    domain, host, port, service, metadata, discovered_at,
                    target_engagement, notes
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """, (
                cred_id,
                credential.credential_type.value,
                credential.source.value,
                credential.username,
                password_encrypted,
                credential.domain,
                credential.host,
                credential.port,
                credential.service,
                json.dumps(credential.metadata),
                credential.discovered_at,
                engagement,
                notes,
            ))

        self._conn.commit()
        logger.info(f"Stored credential {cred_id}")
        return cred_id

    def store_batch(
        self,
        credentials: list[Credential],
        engagement: str | None = None,
    ) -> list[str]:
        """Store multiple credentials."""
        return [self.store(cred, engagement) for cred in credentials]

    def get(self, cred_id: str) -> StoredCredential | None:
        """Get a credential by ID."""
        cursor = self._conn.cursor()
        cursor.execute("SELECT * FROM credentials WHERE id = ?", (cred_id,))
        row = cursor.fetchone()

        if not row:
            return None

        return self._row_to_stored(row)

    def search(
        self,
        credential_type: CredentialType | None = None,
        host: str | None = None,
        service: str | None = None,
        username: str | None = None,
        engagement: str | None = None,
        verified_only: bool = False,
        limit: int = 100,
    ) -> list[StoredCredential]:
        """
        Search for credentials.

        Args:
            credential_type: Filter by type
            host: Filter by host
            service: Filter by service
            username: Filter by username
            engagement: Filter by engagement
            verified_only: Only return verified credentials
            limit: Maximum results

        Returns:
            List of matching StoredCredential objects
        """
        cursor = self._conn.cursor()

        query = "SELECT * FROM credentials WHERE 1=1"
        params = []

        if credential_type:
            query += " AND credential_type = ?"
            params.append(credential_type.value)

        if host:
            query += " AND host LIKE ?"
            params.append(f"%{host}%")

        if service:
            query += " AND service = ?"
            params.append(service)

        if username:
            query += " AND username LIKE ?"
            params.append(f"%{username}%")

        if engagement:
            query += " AND target_engagement = ?"
            params.append(engagement)

        if verified_only:
            query += " AND verified = 1"

        query += " ORDER BY discovered_at DESC LIMIT ?"
        params.append(limit)

        cursor.execute(query, params)
        return [self._row_to_stored(row) for row in cursor.fetchall()]

    def get_for_host(self, host: str) -> list[StoredCredential]:
        """Get all credentials for a specific host."""
        return self.search(host=host)

    def get_for_service(self, service: str) -> list[StoredCredential]:
        """Get all credentials for a specific service."""
        return self.search(service=service)

    def mark_verified(self, cred_id: str, verified: bool = True):
        """Mark a credential as verified/working."""
        cursor = self._conn.cursor()
        cursor.execute(
            "UPDATE credentials SET verified = ?, last_used = ? WHERE id = ?",
            (1 if verified else 0, datetime.now(timezone.utc).isoformat(), cred_id)
        )
        self._conn.commit()

    def mark_used(self, cred_id: str):
        """Update last used timestamp."""
        cursor = self._conn.cursor()
        cursor.execute(
            "UPDATE credentials SET last_used = ? WHERE id = ?",
            (datetime.now(timezone.utc).isoformat(), cred_id)
        )
        self._conn.commit()

    def delete(self, cred_id: str):
        """Delete a credential."""
        cursor = self._conn.cursor()
        cursor.execute("DELETE FROM credentials WHERE id = ?", (cred_id,))
        self._conn.commit()

    def get_stats(self) -> dict[str, Any]:
        """Get credential statistics."""
        cursor = self._conn.cursor()

        stats = {
            "total": 0,
            "verified": 0,
            "by_type": {},
            "by_service": {},
            "by_source": {},
        }

        cursor.execute("SELECT COUNT(*) FROM credentials")
        stats["total"] = cursor.fetchone()[0]

        cursor.execute("SELECT COUNT(*) FROM credentials WHERE verified = 1")
        stats["verified"] = cursor.fetchone()[0]

        cursor.execute("SELECT credential_type, COUNT(*) FROM credentials GROUP BY credential_type")
        for row in cursor.fetchall():
            stats["by_type"][row[0]] = row[1]

        cursor.execute("SELECT service, COUNT(*) FROM credentials WHERE service IS NOT NULL GROUP BY service")
        for row in cursor.fetchall():
            stats["by_service"][row[0]] = row[1]

        cursor.execute("SELECT source, COUNT(*) FROM credentials GROUP BY source")
        for row in cursor.fetchall():
            stats["by_source"][row[0]] = row[1]

        return stats

    def export(
        self,
        file_path: str | Path,
        format: str = "json",
        include_passwords: bool = False,
    ):
        """
        Export credentials to file.

        Args:
            file_path: Output file path
            format: Export format (json, csv)
            include_passwords: Whether to include plaintext passwords
        """
        cursor = self._conn.cursor()
        cursor.execute("SELECT * FROM credentials")
        rows = cursor.fetchall()

        credentials = []
        for row in rows:
            stored = self._row_to_stored(row)
            data = stored.to_dict()
            if include_passwords:
                data["password"] = stored.decrypt_password()
            credentials.append(data)

        file_path = Path(file_path)

        if format == "json":
            with open(file_path, "w", encoding="utf-8") as f:
                json.dump(credentials, f, indent=2)
        elif format == "csv":
            import csv
            with open(file_path, "w", newline="", encoding="utf-8") as f:
                if credentials:
                    writer = csv.DictWriter(f, fieldnames=credentials[0].keys())
                    writer.writeheader()
                    writer.writerows(credentials)

        logger.info(f"Exported {len(credentials)} credentials to {file_path}")

    def _row_to_stored(self, row) -> StoredCredential:
        """Convert database row to StoredCredential."""
        return StoredCredential(
            id=row[0],
            credential_type=CredentialType(row[1]),
            source=CredentialSource(row[2]),
            username=row[3],
            password_encrypted=row[4],
            domain=row[5],
            host=row[6],
            port=row[7],
            service=row[8],
            metadata=json.loads(row[9] or "{}"),
            discovered_at=row[10],
            target_engagement=row[11],
            verified=bool(row[12]),
            last_used=row[13],
            notes=row[14] or "",
        )

    def close(self):
        """Close database connection."""
        self._conn.close()


# Global instance
_global_store: CredentialStore | None = None


def get_store() -> CredentialStore:
    """Get or create the global credential store."""
    global _global_store
    if _global_store is None:
        _global_store = CredentialStore()
    return _global_store


__all__ = [
    "StoredCredential",
    "CredentialStore",
    "get_store",
]
