import type { PluginManifest } from '@@/plugins/manifest';

// Music — the library, the player, and the phone and tablet apps that stream from it.
//
// The second plugin extracted from the platform, on 2026-08-15. Bigger than offscale and, unlike it, not
// a clean cut. It took two passes: the first left three pieces in the platform, and the second moved two
// of them here after the owner read the code and asked why the platform still had files named for music.
// He was right — one of the three "seams" turned out to be dead code holding the door open.
//
//   api/router.ts     the sidecar proxy, built here — thin, and it must never grow music knowledge
//   sidecar/          the whole /api/music contract: indexing, streaming, per-user state
//   db/               music_favorites, _playlists, _playlist_items, _now_playing
//   web/              the library panels; the shell renders the Workspace
//
// ── What stayed in the platform, and why ── (one item, down from three)
//
// 1. ~~cliamp~~ — MOVED HERE, all of it, into `./cliamp/`. The sidecar halves, the relay, the file
//    browser's panel and its `Play` action. `src/servers/sidecar/music/`, `src/servers/api/cliamp/` and
//    `src/servers/api/music/` no longer exist, and `server.tsx` has no cliamp anything. It is PARKED, not
//    working: bringing it back needs a plugin to own a websocket, which is a platform gap.
//
// 2. The dashboard widget (`src/workspaces/widgets/MusicPlayer/`). Plugins cannot contribute widgets and
//    the mechanism was not worth inventing for one.
//
// 3. The global player overlay (`officerdev/src/MusicPlayer/`, mounted by `DashboardLayout`). This was
//    the one open judgement call and it is decided: THE PLAYER STAYS IN THE PLATFORM. Two reasons, and
//    the second is the one that settles it.
//
//    - Moving it needs a shell slot that renders a plugin-provided component on every route. That is
//      exactly the escape hatch this system deleted on purpose — "there is no way to export a component"
//      is what makes "every plugin route is a Workspace" a property of the shape rather than a rule
//      someone has to remember. Reopening it for one plugin is a bad trade.
//    - It would not even work. The widget above imports `useMusicPlayer` and `PlayerTrack` from
//      `officerdev`, and the platform cannot import from a plugin — so the player STATE stays whatever
//      is decided about the UI. Splitting the engine from the state it drives would leave the same seam
//      in a worse place, and two copies of that state would mean two engines.
//
//    The overlay gates on `can('music')`, which resolves against the permission below — registered at
//    install and gone at uninstall. So the seam switches itself off with the plugin, with no code path
//    that knows why.
//
// ── Host dependencies ──
//
// Music is the plugin that made `osDependencies` exist. Offscale was self-sufficient, so until this one
// there was nothing to declare and no reason to build the field — see ./PLUGIN.md.
export const manifest: PluginManifest = {
  publisher: 'officerdev',
  version: '1.0.0',
  platform: '>=1.0.0',

  label: 'Music',
  summary: 'The music library — browse, play, favourites and playlists',
  icon: 'Music',
  color: '#22c55e',

  // One permission gating the whole surface, grantable per role at read or write like every other.
  //
  // The key is `music` and that is not incidental: it is the key the platform's own registry used until
  // this extraction, so every existing `role_capabilities` grant keeps meaning what it meant, and the
  // overlay's `can('music')` keeps resolving. Renaming it would have been a silent data change.
  //
  // `[open]` What a member's grant MEANS here is this plugin's own job and is not finished. Favourites,
  // playlists and now-playing are already per-caller — the sidecar scopes every one of them by the
  // `X-Officer-User` header the proxy injects — while the library itself is one shared index for the
  // household. So "whose row is this" already has a real, non-uniform answer, which is why the platform's
  // side of it is a uniform read/write and nothing more. Designing the rest belongs in these queries.
  permissions: [
    {
      key: 'music',
      label: 'Music',
      description: 'The music library, playback, and your own favourites and playlists',
      // These are the `personal` paths from the registry entry this replaces, carried across verbatim.
      //
      // They are not read-only — they are genuine writes to the CALLER'S own data, which is what made
      // them safe at read level. The manifest deliberately has no `personal` field, and adding one would
      // be designing the per-user visibility model that is explicitly not this extraction's work. It
      // costs nothing to go without: `isRequestAllowedAtLevel` concatenates `personal` and
      // `readOnlyWrites` into a single allow-list, so the two are the same mechanism under two names and
      // a read grant permits exactly the same four paths it permitted yesterday.
      //
      // `/queue` is here because it was there. No such route exists, in the sidecar or anywhere else.
      readOnlyWrites: ['/favorites', '/now-playing', '/playlists', '/queue'],
    },
  ],

  // Both come from one package everywhere, which is luck rather than a rule — hence a name per manager
  // rather than one canonical name. `packages.sh` records why that indirection was rejected.
  //
  // They are declared SEPARATELY even so, because the platform probes binaries and these two fail
  // differently. Losing `ffprobe` is the quiet one: the indexer catches the spawn error and returns a
  // track carrying its filename and nothing else — no title, artist, album, duration or embedded
  // lyrics — then reports success. Losing `ffmpeg` costs cover art and video poster frames, which is at
  // least visible. Naming both means the owner is told which of the two they are missing.
  osDependencies: [
    {
      binary: 'ffprobe',
      reason: 'Reads tags, duration and embedded lyrics. Without it every track indexes as a bare filename.',
      packages: { apt: 'ffmpeg', pacman: 'ffmpeg', dnf: 'ffmpeg', brew: 'ffmpeg' },
    },
    {
      binary: 'ffmpeg',
      reason: 'Compresses cover art for phones and grabs poster frames from videos.',
      packages: { apt: 'ffmpeg', pacman: 'ffmpeg', dnf: 'ffmpeg', brew: 'ffmpeg' },
    },
  ],
};
