"""
Medication Import Script
Fetches medications from OpenFDA Drug Label API and inserts them into MongoDB
as a 'medications' reference collection.

REVERSIBLE: To undo, simply drop the 'medications' collection:
    db.medications.drop()
All documents are tagged with source: "openfda_bulk_import" for easy identification.
"""

import json
import sys
import os
import time
import urllib.request
import urllib.parse
from datetime import datetime

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

from pymongo import MongoClient
from dotenv import load_dotenv

load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env'))

MONGODB_URI = os.getenv('MONGODB_URI')
MONGODB_DB_NAME = os.getenv('MONGODB_DB_NAME', 'hipograf')

# Pharmacological classes to query - covers major drug categories
PHARM_CLASSES = [
    "Nonsteroidal Anti-inflammatory Drug",
    "Corticosteroid",
    "Antiarrhythmic",
    "Platelet Aggregation Inhibitor",
    "Thiazide Diuretic",
    "Azole Antifungal",
    "Central Nervous System Stimulant",
    "Benzodiazepine",
    "Proton Pump Inhibitor",
    "Histamine-2 Receptor Antagonist",
    "Atypical Antipsychotic",
    "Anti-epileptic Agent",
    "Muscle Relaxant",
    "Angiotensin 2 Receptor Blocker",
    "beta-Adrenergic Blocker",
    "Sulfonylurea",
    "Loop Diuretic",
    "Catecholamine",
    "alpha-Adrenergic Agonist",
    "beta-Adrenergic Agonist",
    "Estrogen",
    "HMG-CoA Reductase Inhibitor",
    "Nitrate Vasodilator",
    "Retinoid",
    "Nitroimidazole Antimicrobial",
    "Nucleoside Metabolic Inhibitor",
    "Phosphodiesterase 5 Inhibitor",
    "Aldosterone Antagonist",
    "Xanthine Oxidase Inhibitor",
    "Osmotic Laxative",
    "Dihydropyridine Calcium Channel Blocker",
    "Anti-coagulant",
    "Polyene Antifungal",
    "Stimulant Laxative",
    "alpha-Adrenergic Blocker",
    "Kinase Inhibitor",
    "Progestin",
    "5-alpha Reductase Inhibitor",
    "Androgen",
    "Macrolide Antimicrobial",
    "Fluoroquinolone Antibacterial",
    "Aminoglycoside Antibacterial",
    "Cephalosporin Antibacterial",
    "Angiotensin Converting Enzyme Inhibitor",
    "Typical Antipsychotic",
    "Tetracycline-class Drug",
    "General Anesthetic",
    "Carbonic Anhydrase Inhibitor",
    "Expectorant",
    "Amide Local Anesthetic",
    "Methylxanthine",
    "Mood Stabilizer",
    "Antiseptic",
    "Vitamin D",
    "Serotonin-3 Receptor Antagonist",
    "Insulin Analog",
    "Prostaglandin Analog",
    "Aromatase Inhibitor",
    "Barbiturate",
    "Sulfonamide Antimicrobial",
    "Aminosalicylate",
    "Potassium-sparing Diuretic",
    "Non-narcotic Antitussive",
    "Nitrofuran Antibacterial",
    "Dihydrofolate Reductase Inhibitor Antibacterial",
]

# Map pharm classes to body regions / therapeutic areas
PHARM_CLASS_BODY_MAP = {
    "Nonsteroidal Anti-inflammatory Drug": "whole body",
    "Corticosteroid": "whole body",
    "Antiarrhythmic": "heart",
    "Platelet Aggregation Inhibitor": "blood",
    "Thiazide Diuretic": "kidney",
    "Azole Antifungal": "skin",
    "Central Nervous System Stimulant": "brain",
    "Benzodiazepine": "brain",
    "Proton Pump Inhibitor": "stomach",
    "Histamine-2 Receptor Antagonist": "stomach",
    "Atypical Antipsychotic": "brain",
    "Anti-epileptic Agent": "brain",
    "Muscle Relaxant": "muscles",
    "Angiotensin 2 Receptor Blocker": "heart",
    "beta-Adrenergic Blocker": "heart",
    "Sulfonylurea": "pancreas",
    "Loop Diuretic": "kidney",
    "Catecholamine": "heart",
    "alpha-Adrenergic Agonist": "blood vessels",
    "beta-Adrenergic Agonist": "lungs",
    "Estrogen": "reproductive system",
    "HMG-CoA Reductase Inhibitor": "liver",
    "Nitrate Vasodilator": "heart",
    "Retinoid": "skin",
    "Nitroimidazole Antimicrobial": "whole body",
    "Nucleoside Metabolic Inhibitor": "whole body",
    "Phosphodiesterase 5 Inhibitor": "blood vessels",
    "Aldosterone Antagonist": "kidney",
    "Xanthine Oxidase Inhibitor": "joints",
    "Osmotic Laxative": "intestines",
    "Dihydropyridine Calcium Channel Blocker": "heart",
    "Anti-coagulant": "blood",
    "Polyene Antifungal": "whole body",
    "Stimulant Laxative": "intestines",
    "alpha-Adrenergic Blocker": "blood vessels",
    "Kinase Inhibitor": "whole body",
    "Progestin": "reproductive system",
    "5-alpha Reductase Inhibitor": "prostate",
    "Androgen": "reproductive system",
    "Macrolide Antimicrobial": "whole body",
    "Fluoroquinolone Antibacterial": "whole body",
    "Aminoglycoside Antibacterial": "whole body",
    "Cephalosporin Antibacterial": "whole body",
    "Angiotensin Converting Enzyme Inhibitor": "heart",
    "Typical Antipsychotic": "brain",
    "Tetracycline-class Drug": "whole body",
    "General Anesthetic": "brain",
    "Carbonic Anhydrase Inhibitor": "eye",
    "Expectorant": "lungs",
    "Amide Local Anesthetic": "nerves",
    "Methylxanthine": "lungs",
    "Mood Stabilizer": "brain",
    "Antiseptic": "skin",
    "Vitamin D": "bones",
    "Serotonin-3 Receptor Antagonist": "stomach",
    "Insulin Analog": "pancreas",
    "Prostaglandin Analog": "eye",
    "Aromatase Inhibitor": "whole body",
    "Barbiturate": "brain",
    "Sulfonamide Antimicrobial": "whole body",
    "Aminosalicylate": "intestines",
    "Potassium-sparing Diuretic": "kidney",
    "Non-narcotic Antitussive": "lungs",
    "Nitrofuran Antibacterial": "urinary tract",
    "Dihydrofolate Reductase Inhibitor Antibacterial": "whole body",
}


def clean_text(text, field_name=""):
    """Clean up FDA text — remove section headers, numbering, and artifacts."""
    if not text:
        return ""
    if isinstance(text, list):
        text = " ".join(text)

    import re

    # Strip leading section number + title (e.g., "6 ADVERSE REACTIONS " or "1 INDICATIONS AND USAGE ")
    # Only remove the header portion, keep the content that follows
    headers = (
        'ADVERSE REACTIONS|INDICATIONS AND USAGE|DOSAGE AND ADMINISTRATION|'
        'WARNINGS AND PRECAUTIONS|WARNINGS|CONTRAINDICATIONS|DRUG INTERACTIONS|'
        'OVERDOSAGE|DESCRIPTION|CLINICAL PHARMACOLOGY|USE IN SPECIFIC POPULATIONS|'
        'HOW SUPPLIED|Warnings|Directions|Uses|Active ingredient'
    )
    text = re.sub(rf'^\d*\.?\d*\s*(?:{headers})\s*', '', text, flags=re.IGNORECASE)

    # Remove inline sub-section numbers like "6.1 " at the start of sentences
    text = re.sub(r'(?<!\d)\d+\.\d+\s+', ' ', text)

    # Remove "To report SUSPECTED ADVERSE REACTIONS..." boilerplate
    text = re.sub(r'To report SUSPECTED ADVERSE REACTIONS.*?(?:www\.fda\.gov/medwatch|medwatch)\s*\.?\s*', '', text)

    # Remove cross-references like "[see Warnings and Precautions ( 5.1 )]"
    text = re.sub(r'\[see [^\]]+\]', '', text)
    # Remove parenthetical section refs like "( 6 )" or "( 5.1 )"
    text = re.sub(r'\(\s*\d+(\.\d+)?\s*\)', '', text)

    # Remove table-like artifacts (N=xxx)
    text = re.sub(r'\(N=\d+\)', '', text)

    # Clean up excessive whitespace
    text = re.sub(r'\s+', ' ', text).strip()

    return text


def fetch_drugs_for_class(pharm_class, limit=10):
    """Fetch drug labels from OpenFDA for a given pharmacological class."""
    # OpenFDA pharm_class_epc values include "[EPC]" suffix
    search_term = f'{pharm_class} [EPC]'
    # Manually build URL to control encoding (urllib.parse.urlencode double-encodes)
    encoded_term = urllib.parse.quote(search_term, safe='')
    url = f"https://api.fda.gov/drug/label.json?search=openfda.pharm_class_epc.exact:%22{encoded_term}%22&limit={limit}"

    try:
        req = urllib.request.Request(url, headers={'User-Agent': 'Hipograf/1.0'})
        with urllib.request.urlopen(req, timeout=15) as resp:
            data = json.loads(resp.read().decode())
            return data.get('results', [])
    except Exception as e:
        print(f"  Error fetching {pharm_class}: {e}")
        return []


def extract_medication(result, pharm_class):
    """Extract medication info from an OpenFDA drug label result."""
    openfda = result.get('openfda', {})

    generic_names = openfda.get('generic_name', [])
    brand_names = openfda.get('brand_name', [])
    substance_names = openfda.get('substance_name', [])
    routes = openfda.get('route', [])

    if not generic_names and not brand_names:
        return None

    generic_name = generic_names[0] if generic_names else ""
    brand_name = brand_names[0] if brand_names else ""

    # Build medication document
    med = {
        "genericName": generic_name.title() if generic_name else "",
        "brandName": brand_name.title() if brand_name else "",
        "substanceName": substance_names[0].title() if substance_names else generic_name.title(),
        "route": routes[0].lower() if routes else "unknown",
        "pharmClass": pharm_class,
        "indications": clean_text(result.get('indications_and_usage', '')),
        "dosageAndAdministration": clean_text(result.get('dosage_and_administration', '')),
        "adverseReactions": clean_text(result.get('adverse_reactions', '')),
        "warnings": clean_text(result.get('warnings', '')),
        "contraindications": clean_text(result.get('contraindications', '')),
        "drugInteractions": clean_text(result.get('drug_interactions', '')),
        "overdosage": clean_text(result.get('overdosage', '')),
        "activeIngredient": clean_text(result.get('active_ingredient', '')),
        "source": "openfda_bulk_import",
        "importedAt": datetime.utcnow(),
    }
    return med


def main():
    print("=" * 60)
    print("Medication Import from OpenFDA")
    print("=" * 60)

    client = MongoClient(MONGODB_URI)
    db = client[MONGODB_DB_NAME]
    collection = db['medications']

    # Check if already imported
    existing = collection.count_documents({"source": "openfda_bulk_import"})
    if existing > 0:
        print(f"\nFound {existing} existing imported medications.")
        resp = input("Drop and re-import? (y/n): ").strip().lower()
        if resp != 'y':
            print("Aborted.")
            return
        collection.delete_many({"source": "openfda_bulk_import"})
        print("Cleared existing imports.")

    all_meds = {}  # Deduplicate by generic name, keep best version

    def richness(med):
        """Score how much useful data a medication entry has."""
        fields = ['indications', 'dosageAndAdministration', 'adverseReactions',
                  'warnings', 'contraindications', 'drugInteractions']
        return sum(len(med.get(f, '')) for f in fields)

    for i, pharm_class in enumerate(PHARM_CLASSES):
        print(f"\n[{i+1}/{len(PHARM_CLASSES)}] Fetching: {pharm_class}")
        results = fetch_drugs_for_class(pharm_class, limit=20)
        print(f"  Got {len(results)} results")

        count = 0
        for result in results:
            med = extract_medication(result, pharm_class)
            if med and med['genericName']:
                key = med['genericName'].lower().strip()
                # Keep the version with more populated fields
                if key not in all_meds or richness(med) > richness(all_meds[key]):
                    if key not in all_meds:
                        count += 1
                    all_meds[key] = med
        print(f"  Added {count} unique medications")

        # Rate limiting - OpenFDA allows 240 requests/minute without API key
        time.sleep(0.3)

    print(f"\n{'=' * 60}")
    med_list = list(all_meds.values())
    print(f"Total unique medications collected: {len(med_list)}")

    if med_list:
        # Insert in batches
        batch_size = 100
        for i in range(0, len(med_list), batch_size):
            batch = med_list[i:i + batch_size]
            collection.insert_many(batch)
            print(f"  Inserted batch {i // batch_size + 1} ({len(batch)} meds)")

        # Create index on common query fields
        collection.create_index("genericName")
        collection.create_index("pharmClass")
        collection.create_index("source")

        print(f"\nDone! Inserted {len(med_list)} medications into '{MONGODB_DB_NAME}.medications'")
        print(f"\nTo reverse: db.medications.drop()")
    else:
        print("No medications were collected.")


if __name__ == '__main__':
    main()
