"""
FGIP Export - Export graph to JSON and other formats.

Supports sensitivity gating and assertion level filtering for public exports.

Export modes:
- PRIVATE: Exports everything (internal use)
- PUBLIC: Only exports FACT + INFERENCE where all supporting edges are EVIDENCED
  - HYPOTHESIS edges are NEVER exported publicly
  - AGGRESSIVE tone content is NEVER exported publicly
"""

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

from .models import Sensitivity, AssertionLevel, Visibility
from .narrative import generate_disclaimer


def export_graph(
    conn: sqlite3.Connection,
    output_path: Optional[Path] = None,
    format: str = "json",
    include_sources: bool = True,
    max_sensitivity: Sensitivity = Sensitivity.LOW,
    require_tier_for_high: int = 1,
    public_mode: bool = False,
) -> Dict[str, Any]:
    """
    Export the graph to JSON format.

    PUBLIC MODE RULES:
    - HYPOTHESIS edges are NEVER exported
    - Only FACT + INFERENCE edges with EVIDENCED status are exported
    - Only edges with visibility=PUBLIC are exported
    - Disclaimer is automatically included

    Args:
        conn: SQLite connection
        output_path: Path to write output (None = return dict only)
        format: Export format (currently only "json" supported)
        include_sources: Include source details
        max_sensitivity: Maximum sensitivity level to include
        require_tier_for_high: Required source tier for HIGH sensitivity edges
        public_mode: If True, apply strict public export rules

    Returns:
        Export data as dict
    """
    cursor = conn.cursor()
    cursor.row_factory = sqlite3.Row

    sensitivity_levels = {
        Sensitivity.LOW: 0,
        Sensitivity.MED: 1,
        Sensitivity.HIGH: 2,
    }
    max_level = sensitivity_levels[max_sensitivity]

    # Export nodes
    cursor.execute("SELECT * FROM nodes")
    nodes = []
    for row in cursor.fetchall():
        nodes.append({
            "node_id": row['node_id'],
            "node_type": row['node_type'],
            "name": row['name'],
            "metadata": json.loads(row['metadata']) if row['metadata'] else {},
        })

    # Export edges with sensitivity and public mode filtering
    cursor.execute("SELECT * FROM edges")
    edges = []
    skipped_hypothesis = 0
    skipped_not_evidenced = 0
    skipped_private = 0

    for row in cursor.fetchall():
        edge_sensitivity = Sensitivity[row['sensitivity']] if row['sensitivity'] else Sensitivity.LOW
        edge_level = sensitivity_levels.get(edge_sensitivity, 0)

        # Parse assertion_level and visibility (with defaults for legacy data)
        assertion_level_str = row['assertion_level'] if 'assertion_level' in row.keys() else 'INFERENCE'
        visibility_str = row['visibility'] if 'visibility' in row.keys() else 'PRIVATE'
        assertion_level = AssertionLevel[assertion_level_str] if assertion_level_str else AssertionLevel.INFERENCE
        visibility = Visibility[visibility_str] if visibility_str else Visibility.PRIVATE

        # PUBLIC MODE FILTERS
        if public_mode:
            # Rule 1: HYPOTHESIS edges are NEVER public
            if assertion_level == AssertionLevel.HYPOTHESIS:
                skipped_hypothesis += 1
                continue

            # Rule 2: Only EVIDENCED edges in public mode
            if row['status'] != 'EVIDENCED':
                skipped_not_evidenced += 1
                continue

            # Rule 3: Only PUBLIC visibility edges
            if visibility != Visibility.PUBLIC:
                skipped_private += 1
                continue

        # Skip if above max sensitivity
        if edge_level > max_level:
            continue

        # For HIGH sensitivity, check source tier requirement
        if edge_sensitivity == Sensitivity.HIGH:
            cursor.execute("""
                SELECT MIN(s.tier) as min_tier
                FROM edge_sources es
                JOIN sources s ON es.source_id = s.source_id
                WHERE es.edge_id = ?
            """, (row['edge_id'],))
            tier_row = cursor.fetchone()

            if tier_row['min_tier'] is None or tier_row['min_tier'] > require_tier_for_high:
                # Skip - doesn't meet tier requirement
                continue

        edge_data = {
            "edge_id": row['edge_id'],
            "edge_type": row['edge_type'],
            "from_node_id": row['from_node_id'],
            "to_node_id": row['to_node_id'],
            "status": row['status'],
            "sensitivity": row['sensitivity'],
            "assertion_level": assertion_level_str,
            "visibility": visibility_str,
            "confidence": row['confidence'],
        }

        if row['date_occurred_start']:
            edge_data['date_occurred_start'] = row['date_occurred_start']
        if row['date_occurred_end']:
            edge_data['date_occurred_end'] = row['date_occurred_end']
        if row['notes']:
            edge_data['notes'] = row['notes']

        # Get attributes
        cursor.execute("SELECT key, value, value_type FROM edge_attrs WHERE edge_id = ?", (row['edge_id'],))
        attrs = {}
        for attr_row in cursor.fetchall():
            key = attr_row['key']
            value = attr_row['value']
            vtype = attr_row['value_type']

            if vtype == 'int':
                value = int(value)
            elif vtype == 'float':
                value = float(value)
            elif vtype == 'json':
                value = json.loads(value)

            attrs[key] = value

        if attrs:
            edge_data['attrs'] = attrs

        # Get sources if requested
        if include_sources:
            cursor.execute("""
                SELECT s.source_id, s.source_type, s.title, s.url, s.sha256, s.tier,
                       es.claim_excerpt, es.page_ref
                FROM edge_sources es
                JOIN sources s ON es.source_id = s.source_id
                WHERE es.edge_id = ?
            """, (row['edge_id'],))

            sources = []
            for src_row in cursor.fetchall():
                source = {
                    "source_id": src_row['source_id'],
                    "source_type": src_row['source_type'],
                    "tier": src_row['tier'],
                }
                if src_row['title']:
                    source['title'] = src_row['title']
                if src_row['url']:
                    source['url'] = src_row['url']
                if src_row['sha256']:
                    source['sha256'] = src_row['sha256']
                if src_row['claim_excerpt']:
                    source['claim_excerpt'] = src_row['claim_excerpt']
                if src_row['page_ref']:
                    source['page_ref'] = src_row['page_ref']
                sources.append(source)

            if sources:
                edge_data['sources'] = sources

        edges.append(edge_data)

    # Export claims
    cursor.execute("SELECT * FROM claims")
    claims = []
    for row in cursor.fetchall():
        claim_data = {
            "claim_id": row['claim_id'],
            "claim_text": row['claim_text'],
            "claim_type": row['claim_type'],
            "required_tier": row['required_tier'],
            "status": row['status'],
        }

        # Get linked edges
        cursor.execute("SELECT edge_id FROM claim_edges WHERE claim_id = ?", (row['claim_id'],))
        edge_ids = [r['edge_id'] for r in cursor.fetchall()]
        if edge_ids:
            claim_data['edge_ids'] = edge_ids

        claims.append(claim_data)

    # Build export
    export_data = {
        "format": "fgip_graph",
        "version": "1.0.0",
        "exported_at": datetime.utcnow().isoformat(),
        "export_settings": {
            "max_sensitivity": max_sensitivity.value,
            "include_sources": include_sources,
            "public_mode": public_mode,
        },
        "statistics": {
            "nodes": len(nodes),
            "edges": len(edges),
            "claims": len(claims),
        },
        "nodes": nodes,
        "edges": edges,
        "claims": claims,
    }

    # Add public mode metadata
    if public_mode:
        export_data["disclaimer"] = generate_disclaimer()
        export_data["public_mode_filters"] = {
            "skipped_hypothesis": skipped_hypothesis,
            "skipped_not_evidenced": skipped_not_evidenced,
            "skipped_private_visibility": skipped_private,
        }

    # Write to file if path provided
    if output_path:
        output_path = Path(output_path)
        output_path.parent.mkdir(parents=True, exist_ok=True)

        with open(output_path, 'w') as f:
            json.dump(export_data, f, indent=2)

    return export_data


def export_subgraph(
    conn: sqlite3.Connection,
    center_node: str,
    depth: int = 2,
    output_path: Optional[Path] = None,
) -> Dict[str, Any]:
    """
    Export a subgraph centered on a node.

    Args:
        conn: SQLite connection
        center_node: Node ID to center on
        depth: Number of hops from center
        output_path: Path to write output

    Returns:
        Export data as dict
    """
    cursor = conn.cursor()
    cursor.row_factory = sqlite3.Row

    # BFS to find nodes within depth
    visited_nodes = {center_node}
    frontier = {center_node}
    edge_ids = set()

    for _ in range(depth):
        next_frontier = set()

        for node in frontier:
            # Outgoing edges
            cursor.execute("""
                SELECT edge_id, to_node_id FROM edges WHERE from_node_id = ?
            """, (node,))
            for row in cursor.fetchall():
                edge_ids.add(row['edge_id'])
                if row['to_node_id'] not in visited_nodes:
                    visited_nodes.add(row['to_node_id'])
                    next_frontier.add(row['to_node_id'])

            # Incoming edges
            cursor.execute("""
                SELECT edge_id, from_node_id FROM edges WHERE to_node_id = ?
            """, (node,))
            for row in cursor.fetchall():
                edge_ids.add(row['edge_id'])
                if row['from_node_id'] not in visited_nodes:
                    visited_nodes.add(row['from_node_id'])
                    next_frontier.add(row['from_node_id'])

        frontier = next_frontier

    # Export nodes
    nodes = []
    for node_id in visited_nodes:
        cursor.execute("SELECT * FROM nodes WHERE node_id = ?", (node_id,))
        row = cursor.fetchone()
        if row:
            nodes.append({
                "node_id": row['node_id'],
                "node_type": row['node_type'],
                "name": row['name'],
                "metadata": json.loads(row['metadata']) if row['metadata'] else {},
            })

    # Export edges
    edges = []
    for edge_id in edge_ids:
        cursor.execute("SELECT * FROM edges WHERE edge_id = ?", (edge_id,))
        row = cursor.fetchone()
        if row:
            edges.append({
                "edge_id": row['edge_id'],
                "edge_type": row['edge_type'],
                "from_node_id": row['from_node_id'],
                "to_node_id": row['to_node_id'],
                "status": row['status'],
                "sensitivity": row['sensitivity'],
            })

    export_data = {
        "format": "fgip_subgraph",
        "version": "1.0.0",
        "exported_at": datetime.utcnow().isoformat(),
        "center_node": center_node,
        "depth": depth,
        "statistics": {
            "nodes": len(nodes),
            "edges": len(edges),
        },
        "nodes": nodes,
        "edges": edges,
    }

    if output_path:
        output_path = Path(output_path)
        output_path.parent.mkdir(parents=True, exist_ok=True)

        with open(output_path, 'w') as f:
            json.dump(export_data, f, indent=2)

    return export_data


def export_cytoscape(
    conn: sqlite3.Connection,
    output_path: Path,
) -> Dict[str, Any]:
    """
    Export graph in Cytoscape.js JSON format.

    Args:
        conn: SQLite connection
        output_path: Path to write output

    Returns:
        Cytoscape data dict
    """
    cursor = conn.cursor()
    cursor.row_factory = sqlite3.Row

    elements = []

    # Export nodes
    cursor.execute("SELECT * FROM nodes")
    for row in cursor.fetchall():
        elements.append({
            "data": {
                "id": row['node_id'],
                "label": row['name'],
                "type": row['node_type'],
            }
        })

    # Export edges
    cursor.execute("SELECT * FROM edges")
    for row in cursor.fetchall():
        elements.append({
            "data": {
                "id": row['edge_id'],
                "source": row['from_node_id'],
                "target": row['to_node_id'],
                "label": row['edge_type'],
                "status": row['status'],
            }
        })

    cytoscape_data = {
        "format_version": "1.0",
        "generated_by": "fgip",
        "target_cytoscapejs_version": "~3.0",
        "data": {
            "name": "FGIP Causality Graph",
            "shared_name": "FGIP Graph",
        },
        "elements": elements,
    }

    output_path = Path(output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)

    with open(output_path, 'w') as f:
        json.dump(cytoscape_data, f, indent=2)

    return cytoscape_data
