import hashlib
import os
import subprocess
import sys
from pathlib import Path

import psycopg
from neo4j import GraphDatabase

PROJECT_ROOT = Path(__file__).resolve().parents[1]
ENV_PATH = PROJECT_ROOT / '.env'
PDF_PATH = PROJECT_ROOT / 'Literature' / 'PaperQA Retrieval-Augmented Generative Agent for Scientific Research.pdf'


def load_env_file() -> dict[str, str]:
    parsed: dict[str, str] = {}
    for raw_line in ENV_PATH.read_text().splitlines():
        line = raw_line.strip()
        if not line or line.startswith('#') or '=' not in line:
            continue
        key, value = line.split('=', 1)
        key = key.strip()
        value = value.strip()
        if len(value) >= 2 and value[0] == value[-1] and value[0] in {'"', "'"}:
            value = value[1:-1]
        elif value.endswith('"') and not value.startswith('"'):
            value = value[:-1]
        elif value.startswith('"') and not value.endswith('"'):
            value = value[1:]
        parsed[key] = value
    return parsed


def md5_file(path: Path) -> str:
    hasher = hashlib.md5()
    with path.open('rb') as handle:
        for block in iter(lambda: handle.read(8192), b''):
            hasher.update(block)
    return hasher.hexdigest()


def main() -> None:
    if not PDF_PATH.exists():
        raise FileNotFoundError(f'PDF target not found: {PDF_PATH}')

    env = load_env_file()
    os.environ.update(env)

    neo4j_uri = env['NEO4J_URI']
    neo4j_user = env['NEO4J_USERNAME']
    neo4j_password = env['NEO4J_PASSWORD']
    db_url = env.get('DIRECT_URL') or env['DATABASE_URL']
    file_hash = md5_file(PDF_PATH)

    print(f'target_pdf={PDF_PATH}')
    print(f'target_hash={file_hash}')

    print('Cleaning existing Neo4j and Supabase data for the target hash...')
    with GraphDatabase.driver(neo4j_uri, auth=(neo4j_user, neo4j_password)) as driver:
        with driver.session() as session:
            session.run('MATCH (n:Literature {hash: $hash_val}) DETACH DELETE n', hash_val=file_hash)

    with psycopg.connect(db_url, connect_timeout=10) as conn:
        with conn.cursor() as cur:
            cur.execute('DELETE FROM document_chunks WHERE file_hash = %s', (file_hash,))
            deleted_rows = cur.rowcount
        conn.commit()
    print(f'cleaned_supabase_rows={deleted_rows}')

    print('Running app/services/ingestion.py against the Literature folder...')
    run_result = subprocess.run(
        [sys.executable, 'app/services/ingestion.py'],
        cwd=PROJECT_ROOT,
        text=True,
        capture_output=True,
        env=os.environ.copy(),
    )

    print('--- orchestrator stdout start ---')
    print(run_result.stdout)
    print('--- orchestrator stdout end ---')
    print('--- orchestrator stderr start ---')
    print(run_result.stderr)
    print('--- orchestrator stderr end ---')
    print(f'orchestrator_exit_code={run_result.returncode}')

    if run_result.returncode != 0:
        raise RuntimeError('app.services.ingestion.py failed during end-to-end test')

    with GraphDatabase.driver(neo4j_uri, auth=(neo4j_user, neo4j_password)) as driver:
        with driver.session() as session:
            node_count = session.run(
                'MATCH (n:Literature {hash: $hash_val}) RETURN count(n) AS c',
                hash_val=file_hash,
            ).single()['c']
    print(f'neo4j_node_count={node_count}')
    if node_count <= 0:
        raise RuntimeError('Neo4j node was not created by the orchestrator run')

    with psycopg.connect(db_url, connect_timeout=10) as conn:
        with conn.cursor() as cur:
            cur.execute('SELECT COUNT(*) FROM document_chunks WHERE file_hash = %s', (file_hash,))
            chunk_count = cur.fetchone()[0]
        conn.commit()
    print(f'supabase_chunk_count={chunk_count}')
    if chunk_count <= 0:
        raise RuntimeError('Supabase document_chunks rows were not created')

    print('End-to-end test completed successfully')


if __name__ == '__main__':
    main()
