/* Campaign data — single source of truth (SSoT) for campaigns.

   Why this module exists
   ----------------------
   Previously the desktop Publisher Console and the phone "My Campaigns"
   summary each called their own usePublisherWorkspace(), so they held two
   independent React snapshots of the same conceptual data. They could drift.

   This module makes campaigns a *shared* data model:
   - CANONICAL_CAMPAIGNS / CANONICAL_APPLICATIONS: the seed data.
   - A single module-level store (one object, created once) holding live state.
   - usePublisherWorkspace(): a hook that subscribes every consumer to that one
     store via React.useSyncExternalStore. Desktop console, phone summary, and
     any future surface all read/write the same snapshot.

   It also encodes the "publisher area -> collector mission" connection:
   one canonical campaign (C-2026-0042, Kensington retail status) carries the
   fields the contributor-facing map/mission surface needs (map pin id m4,
   collector copy, reward). mission-home.jsx references this object so both
   sides are literally two faces of the same campaign.

   Load order: this file MUST load before any consumer
   (mission-home.jsx, profile-screen.jsx, publisher-console.jsx).
   It depends on React only, which the CDN loads first.

   Exports (window): CANONICAL_CAMPAIGNS, CANONICAL_APPLICATIONS,
   getCampaignById, getCollectorCampaign, getCollectorMapPin,
   publisherStore, usePublisherWorkspace.
*/

/* ---------- Canonical seed data ----------
   campaign shape:
   { id, title, status, coverage, spend, verified, remaining, area, reward,
     downloaded,
     // collector-facing fields (only on campaigns surfaced to contributors):
     collector?: { mapPinId, mapLabel, missionTitle, missionHint, gp,
                   distance, window } }
*/

const CANONICAL_CAMPAIGNS = [
  {
    id: 'C-2026-0042',
    title: 'Kensington retail status',
    status: 'Live',
    coverage: '72%',
    spend: '$128 / $196',
    verified: '41',
    remaining: '5d 14h',
    area: 'Kensington Market',
    reward: '3x GP',
    downloaded: false,
    // This is the campaign the contributor app shows as a paid mission.
    // mission-home.jsx (CampaignMissionCard + the m4 map pin) reads these
    // fields so the publisher's campaign and the collector's task are the
    // same object, by the same id.
    collector: {
      mapPinId: 'm4',
      mapLabel: 'Retail status check',
      missionTitle: 'Retail status check',
      missionHint: 'Kensington Market campaign · platform verified · credit guaranteed',
      missionBody: 'Verify open/closed status for commissioned retail data. Guaranteed credit after AI validation.',
      gp: 90,
      distance: '120m',
      window: '5d 14h',
    },
  },
  {
    id: 'C-2026-0037',
    title: 'Queen West facade survey',
    status: 'Completed',
    coverage: '94%',
    spend: '$302 / $318',
    verified: '88',
    remaining: 'Completed',
    area: 'Queen West',
    reward: '2x GP',
    downloaded: true,
  },
];

const CANONICAL_APPLICATIONS = [
  { id:'A-2026-011', title:'Kensington retail status', status:'approved', area:'Kensington Market', quote:196, submitted:'May 09', summary:'Verify storefront status and hours for 60 retail POIs.' },
  { id:'A-2026-010', title:'Waterfront access audit', status:'rejected', area:'Toronto Waterfront', quote:340, submitted:'May 04', summary:'Rejected because requested photo scope included private interiors.' },
];

/* ---------- Read helpers (safe to call from any module) ---------- */

function getCampaignById(id) {
  return CANONICAL_CAMPAIGNS.find(c => c.id === id) || null;
}

/* The single campaign that the contributor surfaces (map + mission home)
   render. Identified by carrying a `collector` block. */
function getCollectorCampaign() {
  return CANONICAL_CAMPAIGNS.find(c => c.collector) || null;
}

/* Returns the collector-facing fields, falling back to safe defaults so the
   contributor UI never breaks if seed data changes. */
function getCollectorMapPin() {
  const campaign = getCollectorCampaign();
  const c = (campaign && campaign.collector) || {};
  return {
    id: c.mapPinId || 'm4',
    label: c.mapLabel || 'Retail survey',
    campaignId: campaign ? campaign.id : null,
    area: campaign ? campaign.area : 'Kensington Market',
    reward: campaign ? campaign.reward : '3x GP',
  };
}

/* ---------- The shared store (created exactly once) ----------
   A tiny external store: getSnapshot + subscribe + mutating actions.
   Actions replace state immutably and notify subscribers, which makes every
   useSyncExternalStore consumer re-render with the same data.
*/

function createPublisherStore() {
  let state = {
    verificationStatus: 'unverified',
    companyBalance: 420,
    applications: CANONICAL_APPLICATIONS.map(a => ({ ...a })),
    campaigns: CANONICAL_CAMPAIGNS.map(c => ({ ...c })),
  };
  const listeners = new Set();

  function getState() { return state; }
  function setState(next) {
    state = next;
    listeners.forEach(fn => fn());
  }
  function subscribe(fn) {
    listeners.add(fn);
    return () => listeners.delete(fn);
  }

  const actions = {
    setVerificationStatus(status) {
      const value = typeof status === 'function' ? status(state.verificationStatus) : status;
      setState({ ...state, verificationStatus: value });
    },
    setCompanyBalance(value) {
      const next = typeof value === 'function' ? value(state.companyBalance) : value;
      setState({ ...state, companyBalance: next });
    },
    submitApplication() {
      const app = {
        id: 'A-2026-012',
        title: 'Daikanyama site condition',
        status: 'review',
        area: 'Daikanyama',
        quote: 295,
        submitted: 'Today',
        summary: 'Collect facade photos, access condition, and open/closed status in a 0.47 km2 area.',
      };
      if (!state.applications.find(item => item.id === app.id)) {
        setState({ ...state, applications: [app, ...state.applications] });
      }
      return app;
    },
    updateApplicationStatus(app, status) {
      setState({
        ...state,
        applications: state.applications.map(item => item.id === app.id ? { ...item, status } : item),
      });
      return { ...app, status };
    },
    activateApplication(app, method) {
      const campaign = {
        id: 'C-' + app.id.slice(2),
        title: app.title,
        status: 'Live',
        coverage: '0%',
        spend: `$0 / $${app.quote}`,
        verified: '0',
        remaining: '7d',
        area: app.area,
        reward: '3x GP',
        downloaded: false,
      };
      const nextBalance = method === 'balance'
        ? Math.max(0, state.companyBalance - app.quote)
        : state.companyBalance;
      const nextApplications = state.applications.map(item => item.id === app.id ? { ...item, status: 'paid' } : item);
      const nextCampaigns = state.campaigns.find(item => item.id === campaign.id)
        ? state.campaigns
        : [campaign, ...state.campaigns];
      setState({
        ...state,
        companyBalance: nextBalance,
        applications: nextApplications,
        campaigns: nextCampaigns,
      });
      return campaign;
    },
  };

  return { getState, subscribe, ...actions };
}

/* One instance for the whole prototype. Guard against double-eval (Babel can
   re-run a script tag if included twice) by stashing on window. */
const publisherStore = window.publisherStore || createPublisherStore();

/* ---------- The hook every consumer uses ----------
   Returns the same shape the old usePublisherWorkspace() did, so existing
   callers (PublisherConsole, PublisherCampaignSummary, CampaignWorkspaceRouter)
   need no changes — but now all share publisherStore.
*/

function usePublisherWorkspace() {
  const state = React.useSyncExternalStore(publisherStore.subscribe, publisherStore.getState);

  const verified = state.verificationStatus === 'verified';
  const liveCampaigns = state.campaigns.filter(c => c.status === 'Live');
  const approvedApplications = state.applications.filter(a => a.status === 'approved' || a.status === 'paid').length;

  return {
    verificationStatus: state.verificationStatus,
    setVerificationStatus: publisherStore.setVerificationStatus,
    verified,
    companyBalance: state.companyBalance,
    setCompanyBalance: publisherStore.setCompanyBalance,
    applications: state.applications,
    campaigns: state.campaigns,
    liveCampaigns,
    approvedApplications,
    submitApplication: publisherStore.submitApplication,
    updateApplicationStatus: publisherStore.updateApplicationStatus,
    activateApplication: publisherStore.activateApplication,
  };
}

Object.assign(window, {
  CANONICAL_CAMPAIGNS,
  CANONICAL_APPLICATIONS,
  getCampaignById,
  getCollectorCampaign,
  getCollectorMapPin,
  publisherStore,
  usePublisherWorkspace,
});
