import type { Project } from '@lib/schema';

/* InboxIntel case study. Source: F:\Documents\InboxIntel\InboxIntel + current app mockups. */
export const inboxintel: Project = {
  id: 'inboxintel',
  name: 'InboxIntel',
  order: 2,
  status: 'in-development',
  template: 'case-study',
  stack: [
    { name: '.NET 10', context: { en: 'ASP.NET Core', no: 'ASP.NET Core' } },
    { name: 'React', context: { en: 'TypeScript SPA', no: 'TypeScript-SPA' } },
    { name: 'PostgreSQL', context: { en: 'EF Core · pgvector', no: 'EF Core · pgvector' } },
    { name: 'Ollama', context: { en: 'embeddings · summaries', no: 'embeddings · sammendrag' } },
    { name: 'Clean Architecture' },
    { name: 'Docker' },
  ],
  links: [],
  diagram: {
    viewBox: '0 0 1120 300',
    title: { en: 'InboxIntel architecture', no: 'InboxIntel-arkitektur' },
    desc: {
      en: 'A React SPA calls an ASP.NET Core API layered as Clean Architecture. A hosted background worker syncs the external Gmail API into PostgreSQL with pgvector, and a local Ollama model produces the embeddings and summaries that power semantic search.',
      no: 'En React-app kaller et ASP.NET Core-API bygget som Clean Architecture. En bakgrunnstjeneste synkroniserer det eksterne Gmail-API-et inn i PostgreSQL med pgvector, og en lokal Ollama-modell lager embeddings og sammendrag som driver semantisk søk.',
    },
    nodes: [
      {
        id: 'spa',
        x: 40,
        y: 116,
        w: 170,
        h: 72,
        kind: 'internal',
        label: 'React SPA',
        sub: { en: 'split-view · search', no: 'delt visning · søk' },
      },
      {
        id: 'api',
        x: 290,
        y: 116,
        w: 200,
        h: 72,
        kind: 'primary',
        label: 'ASP.NET Core API',
        sub: { en: 'Clean Architecture', no: 'Clean Architecture' },
      },
      {
        id: 'ollama',
        x: 290,
        y: 232,
        w: 200,
        h: 56,
        kind: 'internal',
        label: 'Ollama',
        sub: { en: 'embeddings · summaries', no: 'embeddings · sammendrag' },
      },
      {
        id: 'pg',
        x: 580,
        y: 48,
        w: 180,
        h: 64,
        kind: 'internal',
        label: 'PostgreSQL',
        sub: { en: 'EF Core · pgvector', no: 'EF Core · pgvector' },
      },
      {
        id: 'worker',
        x: 580,
        y: 160,
        w: 180,
        h: 64,
        kind: 'internal',
        label: 'Sync worker',
        sub: { en: 'hosted · Polly', no: 'hostet · Polly' },
      },
      {
        id: 'gmail',
        x: 900,
        y: 152,
        w: 180,
        h: 72,
        kind: 'external',
        label: 'Gmail API',
        sub: { en: 'external · OAuth2', no: 'eksternt · OAuth2' },
      },
    ],
    edges: [
      { d: 'M210 152 H290', kind: 'flow', label: '/api', labelX: 240, labelY: 144 },
      { d: 'M390 188 V232', kind: 'flow', label: 'AI', labelX: 408, labelY: 216 },
      { d: 'M490 134 L580 92', kind: 'flow' },
      { d: 'M490 170 L580 192', kind: 'flow' },
      { d: 'M670 160 V112', kind: 'flow' },
      { d: 'M760 192 H900', kind: 'external', label: 'sync', labelX: 820, labelY: 184 },
    ],
  },
  media: [
    {
      src: '/assets/projects/inboxintel/inbox.webp',
      width: 1600,
      height: 1000,
      alt: {
        en: 'InboxIntel split view: a persistent message list beside a resizable reading pane with an AI summary',
        no: 'InboxIntel delt visning: en fast meldingsliste ved siden av en justerbar leserute med AI-sammendrag',
      },
      caption: {
        en: 'Split view — read and triage without ever opening a new tab',
        no: 'Delt visning — les og sorter uten å åpne en ny fane',
      },
    },
    {
      src: '/assets/projects/inboxintel/dashboard.webp',
      width: 1600,
      height: 1000,
      alt: {
        en: 'InboxIntel analytics dashboard: inbox-health grade, emails by category, 90-day volume and top senders',
        no: 'InboxIntel dashbord: innboks-helse, e-post per kategori, 90-dagers volum og toppavsendere',
      },
      caption: {
        en: 'Analytics — an inbox-health grade, categories, volume trend and top senders',
        no: 'Analyse — innboks-helse, kategorier, volumtrend og toppavsendere',
      },
    },
    {
      src: '/assets/projects/inboxintel/search.webp',
      width: 1600,
      height: 1000,
      alt: {
        en: 'InboxIntel search: hybrid semantic and full-text results with why-matched highlights and relevance scores',
        no: 'InboxIntel søk: hybrid semantisk og fulltekst med treff-forklaring og relevans-score',
      },
      caption: {
        en: 'Search — hybrid semantic + full-text, with why-matched highlights and relevance',
        no: 'Søk — hybrid semantisk + fulltekst, med treff-forklaring og relevans',
      },
    },
  ],
  content: {
    en: {
      valueProp:
        'A self-hosted inbox intelligence and cleanup tool — semantic search, AI summaries and inbox-health analytics over your mail.',
      cardTeaser:
        'Self-hosted inbox intelligence — hybrid semantic + full-text search, AI summaries and inbox-health analytics, with safe cleanup by design.',
      tldr: {
        what: 'Inbox intelligence: split-view reading, semantic search, AI summaries, inbox-health analytics and safe cleanup.',
        why: 'To practise Clean Architecture properly, and make search useful and destructive operations safe by design.',
        stack: '.NET 10 · PostgreSQL + pgvector · React · Ollama · Docker · Clean Architecture.',
        role: 'Sole architect and developer, front to back.',
      },
      sections: [
        {
          kind: 'problem',
          heading: 'Problem & context',
          anchorId: 'problem',
          blocks: [
            {
              type: 'p',
              text: 'A busy inbox is hard to search and dangerous to clean up — keyword search misses what you meant, and one wrong bulk filter deletes things you can’t get back. I built InboxIntel to make an inbox actually searchable (by meaning, not just words), understandable at a glance, and safe to tidy — all self-hosted, with a clean, testable backend.',
            },
          ],
        },
        {
          kind: 'architecture',
          heading: 'Architecture',
          anchorId: 'architecture',
          blocks: [
            {
              type: 'p',
              text: 'A React SPA over an ASP.NET Core API built as Clean Architecture — the dependency rule points inward (Api → Infrastructure → Application → Domain), and controllers hold no business logic. A hosted background worker syncs Gmail into PostgreSQL; a local Ollama model generates embeddings (stored with pgvector) and message summaries, which power hybrid semantic + full-text search.',
            },
            {
              type: 'ul',
              items: [
                'Reading: a persistent list beside a resizable pane with an AI summary — triage without opening a new tab.',
                'Search: semantic (pgvector) + full-text, with why-matched highlights and a relevance score.',
                'Analytics: an inbox-health grade, emails by category, a 90-day volume trend and top senders.',
                'Cleanup: preview-then-confirm on every destructive action; the AI is advisory only.',
              ],
            },
          ],
        },
        {
          kind: 'decisions',
          heading: 'Key decisions & trade-offs',
          anchorId: 'decisions',
          decisions: [
            {
              n: 1,
              choice: 'Local Ollama embeddings + pgvector for semantic search.',
              alternative: 'A hosted embeddings API and a vector database.',
              rationale:
                'Mail is private, so embeddings stay on my own hardware and live right next to the data in Postgres via pgvector — one datastore, no third party, no per-call cost. The trade-off is running the model myself.',
            },
            {
              n: 2,
              choice: 'Encrypt OAuth refresh tokens at rest and never log them.',
              alternative: 'Store them as plain columns.',
              rationale:
                'Refresh tokens are long-lived keys to someone’s mailbox. They’re encrypted with the ASP.NET Core Data Protection API (AES), with keys persisted to a mounted volume — the single most important security decision in the app.',
            },
            {
              n: 3,
              choice: 'Every destructive action is preview-then-confirm.',
              alternative: 'Delete immediately on request.',
              rationale:
                'All cleanup and unsubscribe actions require a server-side preview and an explicit Confirmed flag. The AI layer is advisory only and can never trigger a deletion.',
            },
          ],
        },
        {
          kind: 'security',
          heading: 'Security & production notes',
          anchorId: 'security',
          blocks: [
            {
              type: 'ul',
              items: [
                'Google-OAuth gated; Gmail scopes are read/modify only — no send scope is ever requested.',
                'Embeddings and summaries are generated locally (Ollama); mail never leaves the box for a third-party model.',
                'Polly retry/backoff against Gmail rate limits; Serilog structured logging that never records tokens.',
                'Clean Architecture keeps the layers testable; integration tests boot the API host and assert authorization.',
              ],
            },
          ],
        },
        {
          kind: 'screenshots',
          heading: 'Screenshots',
          anchorId: 'screenshots',
        },
        {
          kind: 'next',
          heading: 'Status & what’s next',
          anchorId: 'next',
          blocks: [
            {
              type: 'p',
              text: 'In active development. Search, summaries and analytics work end to end; next up is tuning the semantic-recall thresholds, expanding the integration-test suite, and working toward connectors beyond Gmail so it becomes a genuinely universal inbox tool.',
            },
          ],
        },
      ],
    },
    no: {
      valueProp:
        'Et egendriftet verktøy for innboks-intelligens og opprydding — semantisk søk, AI-sammendrag og innboks-helse over e-posten din.',
      cardTeaser:
        'Egendriftet innboks-intelligens — hybrid semantisk + fulltekst-søk, AI-sammendrag og innboks-helse, med trygg opprydding fra bunnen.',
      tldr: {
        what: 'Innboks-intelligens: delt visning, semantisk søk, AI-sammendrag, innboks-helse og trygg opprydding.',
        why: 'For å øve på Clean Architecture skikkelig, gjøre søk nyttig og destruktive operasjoner trygge fra bunnen.',
        stack: '.NET 10 · PostgreSQL + pgvector · React · Ollama · Docker · Clean Architecture.',
        role: 'Eneste arkitekt og utvikler, fra ende til ende.',
      },
      sections: [
        {
          kind: 'problem',
          heading: 'Problem og kontekst',
          anchorId: 'problem',
          blocks: [
            {
              type: 'p',
              text: 'En travel innboks er vanskelig å søke i og farlig å rydde i — nøkkelordsøk bommer på det du mente, og ett feil massefilter sletter ting du ikke får tilbake. Jeg bygde InboxIntel for å gjøre en innboks faktisk søkbar (på mening, ikke bare ord), forståelig med ett blikk, og trygg å rydde i — alt egendriftet, med en ryddig og testbar backend.',
            },
          ],
        },
        {
          kind: 'architecture',
          heading: 'Arkitektur',
          anchorId: 'arkitektur',
          blocks: [
            {
              type: 'p',
              text: 'En React-app over et ASP.NET Core-API bygget som Clean Architecture — avhengighetsregelen peker innover (Api → Infrastructure → Application → Domain), og kontrollerne har ingen forretningslogikk. En bakgrunnstjeneste synkroniserer Gmail inn i PostgreSQL; en lokal Ollama-modell lager embeddings (lagret med pgvector) og sammendrag, som driver hybrid semantisk + fulltekst-søk.',
            },
            {
              type: 'ul',
              items: [
                'Lesing: en fast liste ved siden av en justerbar rute med AI-sammendrag — sorter uten å åpne en ny fane.',
                'Søk: semantisk (pgvector) + fulltekst, med treff-forklaring og relevans-score.',
                'Analyse: en innboks-helsekarakter, e-post per kategori, 90-dagers volumtrend og toppavsendere.',
                'Opprydding: forhåndsvis-så-bekreft på hver destruktive handling; AI-en er kun rådgivende.',
              ],
            },
          ],
        },
        {
          kind: 'decisions',
          heading: 'Viktige valg og avveininger',
          anchorId: 'beslutninger',
          decisions: [
            {
              n: 1,
              choice: 'Lokale Ollama-embeddings + pgvector for semantisk søk.',
              alternative: 'Et hostet embeddings-API og en egen vektordatabase.',
              rationale:
                'E-post er privat, så embeddings blir på min egen maskin og ligger rett ved siden av dataene i Postgres via pgvector — én datalagring, ingen tredjepart, ingen kostnad per kall. Avveiningen er å drifte modellen selv.',
            },
            {
              n: 2,
              choice: 'Kryptere OAuth-refresh-tokens i ro og aldri logge dem.',
              alternative: 'Lagre dem som vanlige kolonner.',
              rationale:
                'Refresh-tokens er langlevde nøkler til noens innboks. De krypteres med ASP.NET Core Data Protection API (AES), med nøkler lagret på et montert volum — det viktigste sikkerhetsvalget i appen.',
            },
            {
              n: 3,
              choice: 'Alle destruktive handlinger er forhåndsvis-så-bekreft.',
              alternative: 'Slette umiddelbart ved forespørsel.',
              rationale:
                'All opprydding og avmelding krever en server-side forhåndsvisning og et eksplisitt Confirmed-flagg. AI-laget er kun rådgivende og kan aldri utløse en sletting.',
            },
          ],
        },
        {
          kind: 'security',
          heading: 'Sikkerhet og drift',
          anchorId: 'sikkerhet',
          blocks: [
            {
              type: 'ul',
              items: [
                'Google-OAuth-beskyttet; Gmail-scopes er kun lese/endre — send-scope blir aldri etterspurt.',
                'Embeddings og sammendrag lages lokalt (Ollama); e-post forlater aldri maskinen til en tredjepartsmodell.',
                'Polly retry/backoff mot Gmails rategrenser; strukturert Serilog-logging som aldri lagrer tokens.',
                'Clean Architecture holder lagene testbare; integrasjonstester starter API-verten og sjekker autorisasjon.',
              ],
            },
          ],
        },
        {
          kind: 'screenshots',
          heading: 'Skjermbilder',
          anchorId: 'skjermbilder',
        },
        {
          kind: 'next',
          heading: 'Status og veien videre',
          anchorId: 'videre',
          blocks: [
            {
              type: 'p',
              text: 'Under aktiv utvikling. Søk, sammendrag og analyse fungerer ende til ende; neste steg er å justere tersklene for semantisk gjenfinning, utvide integrasjonstestene, og jobbe mot koblinger utover Gmail slik at det blir et virkelig universelt innboks-verktøy.',
            },
          ],
        },
      ],
    },
  },
};
