// JURISDICTIONS PLATFORM — pub/sub store + API + cross-platform bridges
(function(){

const clone = (x) => JSON.parse(JSON.stringify(x));
const seed = window.JURISDICTIONS_DATA;

const state = {
  courts:         clone(seed.courts),
  judges:         clone(seed.judges),
  rules:          clone(seed.rules),
  deadlineRules:  clone(seed.deadlineRules),
  standingOrders: clone(seed.standingOrders),
  admissions:     clone(seed.admissions),
  proHacVice:     clone(seed.proHacVice),
  forms:          clone(seed.forms),
  formPacks:      clone(seed.formPacks || []),
  formCompletions:clone(seed.formCompletions || []),
  favoriteFormIds:clone(seed.favoriteFormIds || []),
  venueAnalytics: clone(seed.venueAnalytics),
  alerts:         clone(seed.alerts),
  kpis:           clone(seed.kpis),
  auditLog: [],
  savedViews: [
    { id:'SV-1', name:'CLE Due < 60 days', entity:'admissions', filter:{ cleDueWithinDays:60 }, system:true },
    { id:'SV-2', name:'Pending Pro Hac Vice', entity:'phv', filter:{ status:'Pending' }, system:true },
    { id:'SV-3', name:'Federal Courts Only', entity:'courts', filter:{ type:'Federal' }, system:true },
    { id:'SV-4', name:'Plaintiff-friendly MSJ (>0.30 grant)', entity:'analytics', filter:{ msjGrantRateLt:0.30 }, system:true },
  ],
};

const bus = {
  _subs: {},
  on(topic, fn) {
    if (!this._subs[topic]) this._subs[topic] = new Set();
    this._subs[topic].add(fn);
    return () => this._subs[topic].delete(fn);
  },
  emit(topic, data) {
    (this._subs[topic] || []).forEach(fn => { try { fn(data); } catch (e) { console.error(e); } });
    (this._subs['*']    || []).forEach(fn => { try { fn({ topic, data }); } catch (e) { console.error(e); } });
    try { window.dispatchEvent(new CustomEvent('arbiter:jx.' + topic, { detail: data })); } catch (e) {}
  },
};

const recompute = () => {
  const activeAdmissions = state.admissions.filter(a => a.status === 'Active').length;
  const cleDueSoon = state.admissions.filter(a => {
    if (!a.cleDue) return false;
    const d = Math.round((new Date(a.cleDue) - new Date()) / 86400000);
    return d <= 60 && d >= 0;
  }).length;
  const phvPending = state.proHacVice.filter(p => p.status === 'Pending').length;
  const criticalAlerts = state.alerts.filter(a => a.severity === 'Critical' || a.severity === 'High').length;
  state.kpis = {
    courts:          state.courts.length,
    judges:          state.judges.length,
    rules:           state.rules.length,
    activeAdmissions,
    cleDueSoon,
    phvPending,
    forms:           state.forms.length,
    alerts:          state.alerts.length,
    criticalAlerts,
  };
};

const audit = (action, entity, id, meta) => {
  const entry = {
    id: 'AUD-' + Date.now() + '-' + Math.floor(Math.random() * 1000),
    action, entity, id, meta: meta || {},
    at: new Date().toISOString(),
    actor: 'M. Kirkland',
  };
  state.auditLog.unshift(entry);
  if (state.auditLog.length > 500) state.auditLog.length = 500;
  bus.emit('audit.logged', entry);
};

const API = {
  // Courts
  addCourt(payload) {
    const id = 'C-' + (payload.short || 'NEW').toUpperCase().replace(/\s+/g, '-');
    const court = { id, motionPractice:'TBD', efileMandatory:true, ...payload };
    state.courts.push(court);
    recompute();
    audit('create', 'court', id, { name: court.name });
    bus.emit('court.created', court);
    return court;
  },
  updateCourt(id, patch) {
    const c = state.courts.find(x => x.id === id);
    if (!c) return null;
    Object.assign(c, patch);
    audit('update', 'court', id, patch);
    bus.emit('court.updated', c);
    return c;
  },

  // Judges
  addJudge(payload) {
    const id = payload.id || 'J-' + Math.random().toString(36).slice(2, 7).toUpperCase();
    const judge = { motionStats:{granted:0,denied:0,partial:0}, standingOrders:[], recusalTopics:[], caseload:0, ...payload, id };
    state.judges.push(judge);
    recompute();
    audit('create', 'judge', id, { name: judge.name });
    bus.emit('judge.created', judge);
    return judge;
  },
  updateJudge(id, patch) {
    const j = state.judges.find(x => x.id === id);
    if (!j) return null;
    Object.assign(j, patch);
    audit('update', 'judge', id, patch);
    bus.emit('judge.updated', j);
    return j;
  },
  addStandingOrder(judgeId, payload) {
    const id = 'SO-' + Date.now();
    const so = { id, judgeId, date: new Date().toISOString().slice(0,10), ...payload };
    state.standingOrders.push(so);
    const j = state.judges.find(x => x.id === judgeId);
    if (j) {
      j.standingOrders = j.standingOrders || [];
      j.standingOrders.push(id);
    }
    audit('create', 'standingOrder', id, { judgeId, title: so.title });
    bus.emit('standingOrder.created', so);
    return so;
  },

  // Rules
  addRule(payload) {
    const id = 'R-' + Date.now();
    const rule = { id, appliesTo: [], tags: [], ...payload };
    state.rules.push(rule);
    recompute();
    audit('create', 'rule', id, { name: rule.name });
    bus.emit('rule.created', rule);
    return rule;
  },

  // Admissions
  addAdmission(payload) {
    const id = 'AD-' + Date.now();
    const ad = { id, status:'Pending', goodStanding:false, cleHoursCompleted:0, ...payload };
    state.admissions.push(ad);
    recompute();
    audit('create', 'admission', id, { attorneyName: ad.attorneyName, courtId: ad.courtId });
    bus.emit('admission.created', ad);
    return ad;
  },
  logCle(admissionId, hours, topic) {
    const ad = state.admissions.find(a => a.id === admissionId);
    if (!ad) return null;
    ad.cleHoursCompleted = (ad.cleHoursCompleted || 0) + hours;
    if (ad.cleHoursCompleted >= ad.cleHoursRequired && ad.status === 'CLE Due') {
      ad.status = 'Active';
    }
    audit('cle.logged', 'admission', admissionId, { hours, topic });
    bus.emit('cle.logged', { admissionId, hours, topic, total: ad.cleHoursCompleted });
    return ad;
  },
  renewAdmission(admissionId, newRenewalDate) {
    const ad = state.admissions.find(a => a.id === admissionId);
    if (!ad) return null;
    ad.renewalDate = newRenewalDate;
    ad.status = 'Active';
    ad.goodStanding = true;
    audit('renew', 'admission', admissionId, { renewalDate: newRenewalDate });
    bus.emit('admission.renewed', ad);
    return ad;
  },

  // Pro Hac Vice
  fileProHacVice(payload) {
    const id = 'PHV-' + Date.now();
    const phv = {
      id,
      status: 'Pending',
      filedDate: new Date().toISOString().slice(0,10),
      grantedDate: null,
      fee: 300,
      ...payload,
    };
    state.proHacVice.push(phv);
    state.alerts.push({
      id:'AL-'+Date.now(),
      type:'PHV_FILED',
      severity:'Info',
      message:`Pro hac vice filed for ${phv.attorneyName} in ${phv.courtId}`,
      relatedId: id,
      createdAt: new Date().toISOString().slice(0,10),
    });
    recompute();
    audit('file', 'phv', id, { attorneyName: phv.attorneyName, courtId: phv.courtId, matterId: phv.matterId });
    bus.emit('phv.filed', phv);
    return phv;
  },
  setPhvStatus(id, status, meta) {
    const phv = state.proHacVice.find(p => p.id === id);
    if (!phv) return null;
    phv.status = status;
    if (status === 'Granted') {
      phv.grantedDate = new Date().toISOString().slice(0,10);
      const exp = new Date();
      exp.setFullYear(exp.getFullYear() + 2);
      phv.expiresDate = exp.toISOString().slice(0,10);
    }
    if (status === 'Denied' && meta && meta.reason) phv.denialReason = meta.reason;
    recompute();
    audit('status', 'phv', id, { status });
    bus.emit('phv.statusChanged', phv);
    return phv;
  },

  // Forms
  addForm(payload) {
    const id = 'F-' + Date.now();
    const form = { id, fillable:true, fileType:'PDF', ...payload };
    state.forms.push(form);
    recompute();
    audit('create', 'form', id, { name: form.name });
    bus.emit('form.created', form);
    return form;
  },

  // Favorites
  toggleFavorite(formId) {
    const i = state.favoriteFormIds.indexOf(formId);
    if (i >= 0) {
      state.favoriteFormIds.splice(i, 1);
      audit('unfavorite', 'form', formId);
      bus.emit('form.unfavorited', { formId });
    } else {
      state.favoriteFormIds.push(formId);
      audit('favorite', 'form', formId);
      bus.emit('form.favorited', { formId });
    }
    return state.favoriteFormIds.slice();
  },

  // Form packs
  createPack(payload) {
    const id = 'PK-' + Date.now();
    const pack = { id, estMinutes:0, category:'Custom', formIds:[], ...payload };
    state.formPacks.push(pack);
    audit('create', 'formPack', id, { name: pack.name });
    bus.emit('formPack.created', pack);
    return pack;
  },

  // Form completion tracking
  markFormCompleted(matterId, matterName, formId, actor) {
    let fc = state.formCompletions.find(x => x.matterId === matterId && x.formId === formId);
    const now = new Date().toISOString().slice(0,10);
    if (fc) {
      fc.status = 'Complete';
      fc.completedAt = now;
      fc.completedBy = actor || 'M. Kirkland';
    } else {
      fc = { id:'FC-'+Date.now(), matterId, matterName, formId, status:'Complete', completedAt: now, completedBy: actor || 'M. Kirkland' };
      state.formCompletions.push(fc);
    }
    audit('complete', 'form', formId, { matterId });
    bus.emit('form.completed', fc);
    return fc;
  },
  setFormStatus(matterId, matterName, formId, status, actor) {
    let fc = state.formCompletions.find(x => x.matterId === matterId && x.formId === formId);
    if (!fc) {
      fc = { id:'FC-'+Date.now(), matterId, matterName, formId, status, completedAt: status === 'Complete' ? new Date().toISOString().slice(0,10) : null, completedBy: actor || null };
      state.formCompletions.push(fc);
    } else {
      fc.status = status;
      if (status === 'Complete') {
        fc.completedAt = new Date().toISOString().slice(0,10);
        fc.completedBy = actor || fc.completedBy || 'M. Kirkland';
      }
    }
    audit('status', 'form', formId, { matterId, status });
    bus.emit('form.statusChanged', fc);
    return fc;
  },

  // Assemble a package — returns manifest of forms, signatures, attachments, merge-field preview
  assembleFormPackage(formIds, matter) {
    const selected = formIds.map(id => state.forms.find(f => f.id === id)).filter(Boolean);
    const signatures = Array.from(new Set(selected.flatMap(f => f.signaturesRequired || [])));
    const attachments = Array.from(new Set(selected.flatMap(f => f.attachments || [])));
    const totalMinutes = selected.reduce((s, f) => s + (f.estMinutes || 0), 0);
    const totalPages = selected.reduce((s, f) => s + (f.pageCount || 0), 0);
    const efileOk = selected.every(f => f.efileCompatible);
    const m = matter || {};
    const fieldPreview = selected.map(f => ({
      formId: f.id,
      code: f.code,
      name: f.name,
      values: (f.fields || []).map(fd => ({
        name: fd.name,
        label: fd.label,
        required: fd.required,
        mergedValue: fd.mergeFrom && m[fd.mergeFrom.split('.')[1]] ? m[fd.mergeFrom.split('.')[1]] : null,
      })),
    }));
    const pkg = {
      id: 'PKG-' + Date.now(),
      forms: selected,
      signatures, attachments, totalMinutes, totalPages, efileOk,
      fieldPreview, matter: m,
      assembledAt: new Date().toISOString(),
    };
    audit('assemble', 'formPackage', pkg.id, { count: selected.length });
    bus.emit('formPackage.assembled', pkg);
    return pkg;
  },

  // Deadline calculation — key feature
  calcDeadline(ruleId, triggerDate) {
    const r = state.deadlineRules.find(x => x.id === ruleId);
    if (!r) return null;
    const base = new Date(triggerDate);
    let result = new Date(base);
    result.setDate(result.getDate() + r.offsetDays);
    if (r.excludeWeekends) {
      while (result.getDay() === 0 || result.getDay() === 6) {
        result.setDate(result.getDate() + (r.offsetDays >= 0 ? 1 : -1));
      }
    }
    bus.emit('deadline.calculated', { ruleId, triggerDate, result: result.toISOString().slice(0,10) });
    return {
      rule: r,
      triggerDate,
      deadline: result.toISOString().slice(0,10),
      daysUntil: Math.round((result - new Date()) / 86400000),
    };
  },
  deadlinesForCourt(courtId) {
    const court = state.courts.find(c => c.id === courtId);
    const type = court ? court.type : null;
    const wildcard = type ? ('*-' + type) : null;
    return state.deadlineRules.filter(r => r.courtId === courtId || r.courtId === wildcard);
  },

  // Alerts
  dismissAlert(id) {
    const i = state.alerts.findIndex(a => a.id === id);
    if (i < 0) return;
    state.alerts.splice(i, 1);
    recompute();
    audit('dismiss', 'alert', id);
    bus.emit('alert.dismissed', { id });
  },

  // Saved views
  saveView(view) {
    const id = 'SV-' + Date.now();
    const sv = { id, system:false, ...view };
    state.savedViews.push(sv);
    audit('create', 'savedView', id, { name: sv.name });
    bus.emit('view.saved', sv);
    return sv;
  },
  deleteView(id) {
    const v = state.savedViews.find(v => v.id === id);
    if (!v || v.system) return false;
    state.savedViews = state.savedViews.filter(x => x.id !== id);
    audit('delete', 'savedView', id);
    bus.emit('view.deleted', { id });
    return true;
  },

  // Analytics — judge comparison
  compareJudges(ids) {
    return ids.map(id => state.judges.find(j => j.id === id)).filter(Boolean);
  },
  rankJudgesBy(metric) {
    const getter = {
      msjGrant:     j => j.summaryJudgmentGrantRate || 0,
      avgDisposition: j => j.avgDispositionDays || 0,
      caseload:     j => j.caseload || 0,
      rating:       j => j.rating || 0,
    }[metric];
    if (!getter) return [];
    return [...state.judges].sort((a, b) => getter(b) - getter(a));
  },

  // Venue analysis — best venue for plaintiff/defendant given case type
  recommendVenue({ side = 'plaintiff' } = {}) {
    const pool = state.venueAnalytics.map(v => {
      const c = state.courts.find(cc => cc.id === v.courtId);
      return { ...v, court: c };
    });
    return pool.sort((a, b) => {
      const aScore = side === 'plaintiff'
        ? (a.plaintiffVerdictRate - a.msjGrantRate * 0.5)
        : (a.msjGrantRate - a.plaintiffVerdictRate * 0.5);
      const bScore = side === 'plaintiff'
        ? (b.plaintiffVerdictRate - b.msjGrantRate * 0.5)
        : (b.msjGrantRate - b.plaintiffVerdictRate * 0.5);
      return bScore - aScore;
    });
  },

  // Cross-entity queries
  judgesForCourt(courtId) {
    return state.judges.filter(j => j.courtId === courtId);
  },
  rulesForCourt(courtId) {
    return state.rules.filter(r => r.courtId === courtId || (r.appliesTo || []).includes(courtId));
  },
  admissionsForAttorney(attorneyId) {
    return state.admissions.filter(a => a.attorneyId === attorneyId);
  },
  standingOrdersForJudge(judgeId) {
    return state.standingOrders.filter(so => so.judgeId === judgeId);
  },

  // Snapshot
  snapshot() { return clone(state); },
  state,
  bus,
};

window.JurisdictionsStore = API;

// React hook
window.useJxStore = (topics) => {
  const [, force] = React.useReducer(x => x + 1, 0);
  React.useEffect(() => {
    const list = topics && topics.length ? topics : ['*'];
    const offs = list.map(t => bus.on(t, () => force()));
    return () => offs.forEach(off => off && off());
  }, []);
  return state;
};

// Cross-platform bridges
// 1. When Docket adds a deadline, auto-link to jurisdiction deadline rule
window.addEventListener('arbiter:docket.deadline.created', (ev) => {
  const dl = ev.detail && (ev.detail.deadline || ev.detail);
  if (!dl || !dl.courtId) return;
  const rules = API.deadlinesForCourt(dl.courtId);
  bus.emit('docket.deadline.enriched', { deadline: dl, applicableRules: rules });
});

// 2. When Tasks flags a court filing, surface jurisdiction rules
window.addEventListener('arbiter:task.created', (ev) => {
  const t = ev.detail;
  if (!t) return;
  if (t.category === 'Filing' && t.courtId) {
    const court = state.courts.find(c => c.id === t.courtId);
    if (court) bus.emit('task.court.linked', { taskId: t.id, court });
  }
});

// 3. When a matter opens, emit applicable jurisdictions context
window.addEventListener('arbiter:matter.opened', (ev) => {
  const m = ev.detail;
  if (!m || !m.courtId) return;
  const court = state.courts.find(c => c.id === m.courtId);
  const judges = court ? API.judgesForCourt(court.id) : [];
  const rules  = court ? API.rulesForCourt(court.id)  : [];
  bus.emit('matter.jurisdictions.context', { matterId: m.id, court, judges, rules });
});

// Seed audit log from existing items for visibility
state.courts.forEach(c => audit('seeded', 'court', c.id, { name: c.name }));
state.judges.forEach(j => audit('seeded', 'judge', j.id, { name: j.name }));
state.admissions.forEach(a => audit('seeded', 'admission', a.id, { attorneyName: a.attorneyName }));

})();
