#!/usr/bin/env python3
"""
01_fetch_sequences.py  ->  parts.json

Assemble every sequence part of the single-vector trastuzumab mAb expression
construct from CANONICAL, citable sources (cached under refs/). No sequence is
typed from memory: coding parts use structure/UniProt-verified protein, regulatory
parts are sliced from fully-annotated reference plasmids.

Sources (cached in refs/ ; re-fetch commands in RERUN_NOTES.md):
  - Trastuzumab Fab VH/VL + full kappa LC ....... PDB 1N8Z (Cho et al. 2003)
  - Human IgG1 heavy constant (CH1-h-CH2-CH3) ... UniProt P01857 (secreted form)
  - Human kappa constant (cross-check) .......... UniProt P01834
  - EMCV IRES (nt 260-848) ...................... GenBank M81861 (EMCV-R)
  - CMV enh+prom, bGH pA, SV40 prom, SV40 pA,
    pUC ori, AmpR(bla)+prom ..................... pcDNA3.1(+) (SnapGene; == NCBI MN996867.1)
  - chimeric intron ............................. pCI-neo (SnapGene; Promega)
  - WPRE ........................................ pLVX-Puro (SnapGene)
  - Puromycin N-acetyltransferase (pac/PuroR) ... UniProt P13249
  - Murine Ig-kappa secretion signal peptide .... standard pSecTag/Ig-kappa leader
"""
import json, os, sys
from Bio import SeqIO

HERE = os.path.dirname(os.path.abspath(__file__))
REF  = os.path.join(HERE, "refs")

def snap(fn):
    return SeqIO.read(os.path.join(REF, fn), "snapgene")

def feat_seq(rec, label):
    for f in rec.features:
        if str(f.qualifiers.get("label", ["?"])[0]) == label:
            return str(f.extract(rec.seq)).upper()
    raise KeyError(f"feature {label!r} not found in {rec.id}")

# ---------------------------------------------------------------- regulatory DNA
pcdna = snap("snap_pcdna.bin")              # pcDNA3.1(+), 5428 bp
seq   = str(pcdna.seq).upper()
CMV   = seq[234:818]                         # CMV enhancer [234:614] + promoter [614:818]
BGHpA = seq[1027:1252]                       # bGH poly(A) signal
SV40p = seq[1739:2069]                       # SV40 early promoter (incl. SV40 ori)
SV40pA= seq[3103:3225]                       # SV40 poly(A) signal
PUCori= seq[3675:4261]                       # pUC/ColE1 origin
def rc(s):
    import re
    comp={'A':'T','T':'A','G':'C','C':'G','N':'N'}
    return "".join(comp[b] for b in reversed(s))
AMPR  = rc(seq[4431:5397])                    # AmpR promoter(105) + bla CDS(861), coding orientation

INTRON = feat_seq(snap("snap_pCI-neo.bin"), "chimeric intron")   # 133 bp
WPRE   = feat_seq(snap("snap_v_pLVX-Puro.bin"), "WPRE")          # 589 bp

IRES = str(SeqIO.read(os.path.join(REF,"EMCV_IRES_M81861_260-848.gb"),"genbank").seq).upper()

# ---------------------------------------------------------------- proteins
def aa(fasta):
    return str(SeqIO.read(os.path.join(REF, fasta), "fasta").seq)

# 1N8Z chains (verified by fetch earlier; embedded here from the RCSB FASTA)
H1N8Z = ("EVQLVESGGGLVQPGGSLRLSCAASGFNIKDTYIHWVRQAPGKGLEWVARIYPTNGYTRYADSVKGRFTIS"
         "ADTSKNTAYLQMNSLRAEDTAVYYCSRWGGDGFYAMDYWGQGTLVTVSSASTKGPSVFPLAPSSKSTSGGT"
         "AALGCLVKDYFPEPVTVSWNSGALTSGVHTFPAVLQSSGLYSLSSVVTVPSSSLGTQTYICNVNHKPSNTK"
         "VDKKVEP")                                   # VH(1-120) + CH1 + upper hinge (219 aa)
VH   = H1N8Z[:120]                                    # trastuzumab VH
LC   = ("DIQMTQSPSSLSASVGDRVTITCRASQDVNTAVAWYQQKPGKAPKLLIYSASFLYSGVPSRFSGSRSGTDF"
        "TLTISSLQPEDFATYYCQQHYTTPPTFGQGTKVEIKRTVAAPSVFIFPPSDEQLKSGTASVVCLLNNFYPR"
        "EAKVQWKVDNALQSGNSQESVTEQDSKDSTYSLSSTLTLSKADYEKHKVYACEVTHQGLSSPVTKSFNRGEC")  # 1N8Z light, 214 aa

P01857 = aa("P01857.fasta")                           # IGHG1, membrane isoform (399 aa)
P01834 = aa("P01834.fasta")                           # IGKC (kappa constant)
PAC    = aa("pac_P13249.fasta")                       # PuroR, 199 aa
SP     = "METDTLLLWVLLLWVPGSTG"                        # murine Ig-kappa leader (20 aa)

# Build secreted human IgG1 constant from P01857: CH1..CH3 ending SLSLSPGK
i = P01857.index("SLSLSP") + len("SLSLSP")
IGHG1_secreted = P01857[:i] + "GK"                    # truncate membrane tail, add secreted C-terminus
HC_mature = VH + IGHG1_secreted                        # trastuzumab VH + human IgG1 (secreted)

# -------- consistency checks against the independent authoritative sources -----
checks = []
def chk(name, cond):
    checks.append((name, bool(cond))); print(("PASS" if cond else "FAIL"), name)

chk("1N8Z heavy Fab CH1 == P01857 N-terminus (overlap 99 aa)",
    H1N8Z[120:219] == P01857[:99])
chk("1N8Z light constant == UniProt P01834 (kappa C)",
    LC[107:] == P01834)
chk("Trastuzumab VH ends WGQGTLVTVSS (CDR-H3 WGGDGFYAMDY present)",
    VH.endswith("WGQGTLVTVSS") and "WGGDGFYAMDY" in VH)
chk("HC secreted C-terminus == ...SLSLSPGK", HC_mature.endswith("SLSLSPGK"))
chk("EMCV IRES ends in initiator ATG (fuse point for LC)", IRES.endswith("ATG"))
chk("All proteins are clean 20-aa-alphabet", all(set(p) <= set("ACDEFGHIKLMNPQRSTVWY")
                                                 for p in [SP,HC_mature,LC,PAC]))
chk("Regulatory DNA lengths sane",
    len(CMV)==584 and len(BGHpA)==225 and len(SV40p)==330 and len(SV40pA)==122
    and len(PUCori)==586 and len(AMPR)==966 and len(INTRON)==133 and len(WPRE)==589
    and len(IRES)==589)

if not all(c for _,c in checks):
    print("\nABORT: a consistency check failed.", file=sys.stderr); sys.exit(1)

# ---------------------------------------------------------------- emit parts.json
parts = {
  "metadata": {
    "construct": "Single-vector trastuzumab IgG1 mAb expression vector for CHO-K1 (Golden Gate / BsaI)",
    "architecture": "CMV -> chimeric intron -> [Kozak] SP-HC -> EMCV IRES -> SP-LC -> WPRE -> bGH pA ; "
                    "SV40p -> [Kozak] pac -> SV40 pA ; pUC ori ; AmpR",
    "host_expression": "Cricetulus griseus CHO-K1", "host_propagation": "E. coli",
  },
  "proteins": {
    "signal_peptide": {"aa": SP, "len": len(SP), "source": "Murine Ig-kappa V-region secretion leader (pSecTag-class)"},
    "heavy_chain":    {"aa": SP+HC_mature, "mature_aa": HC_mature, "len": len(SP+HC_mature),
                       "vh": VH, "constant": IGHG1_secreted,
                       "source": "Trastuzumab VH (PDB 1N8Z) + human IGHG1 secreted constant (UniProt P01857); N-term murine Ig-kappa leader"},
    "light_chain":    {"aa": SP+LC, "mature_aa": LC, "len": len(SP+LC),
                       "vl": LC[:107], "constant": LC[107:],
                       "source": "Trastuzumab VL + kappa constant (PDB 1N8Z light chain; Ckappa == UniProt P01834); N-term murine Ig-kappa leader"},
    "pac":            {"aa": PAC, "len": len(PAC), "source": "Puromycin N-acetyltransferase, UniProt P13249 (Streptomyces alboniger)"},
  },
  "dna_parts": {
    "CMV_enh_prom": {"seq": CMV,   "recodable": False, "role": "promoter/enhancer (cap-dependent HC)", "source": "pcDNA3.1(+) 234-818 (HCMV IE)"},
    "chimeric_intron":{"seq":INTRON,"recodable": False, "role": "5'UTR intron (expression boost)", "source": "pCI-neo chimeric intron (Promega)"},
    "EMCV_IRES":    {"seq": IRES,  "recodable": False, "role": "IRES (cap-independent LC); 3' ATG = LC start", "source": "GenBank M81861 nt 260-848 (EMCV-R)"},
    "WPRE":         {"seq": WPRE,  "recodable": False, "role": "post-transcriptional regulatory element", "source": "pLVX-Puro WPRE"},
    "bGH_polyA":    {"seq": BGHpA, "recodable": False, "role": "poly(A) for antibody transcript", "source": "pcDNA3.1(+) bGH pA"},
    "SV40_promoter":{"seq": SV40p, "recodable": False, "role": "promoter for puro cassette", "source": "pcDNA3.1(+) SV40 early promoter"},
    "SV40_polyA":   {"seq": SV40pA,"recodable": False, "role": "poly(A) for puro cassette", "source": "pcDNA3.1(+) SV40 pA"},
    "pUC_ori":      {"seq": PUCori,"recodable": False, "role": "E. coli origin of replication", "source": "pcDNA3.1(+) pUC ori"},
    "AmpR":         {"seq": AMPR,  "recodable": "ecoli", "role": "ampicillin resistance (E. coli selection)", "source": "pcDNA3.1(+) AmpR promoter+bla CDS"},
  },
  "consistency_checks": {n: c for n, c in checks},
}
with open(os.path.join(HERE, "parts.json"), "w") as fh:
    json.dump(parts, fh, indent=2)

print("\nSummary:")
print(f"  HC full ORF: {len(SP+HC_mature)} aa  (SP{len(SP)} + VH{len(VH)} + C{len(IGHG1_secreted)})")
print(f"  LC full ORF: {len(SP+LC)} aa  (SP{len(SP)} + VL107 + Ck{len(LC)-107})")
print(f"  pac: {len(PAC)} aa")
reg = sum(len(v['seq']) for v in parts['dna_parts'].values())
print(f"  regulatory/backbone DNA total: {reg} bp across {len(parts['dna_parts'])} parts")
print("  wrote parts.json")
