// Dev-only session seeding — shared by the jest integration tier and the
// Maestro E2E tier.
//
// This is the entire login surface for both automated tiers. We never drive
// the OAuth UI, never mint a token at runtime — `scripts/dev/bootstrap-test-
// rig.sh` does that once, the rig file persists the result, and the seed
// starts from "user is logged in, registry is warm." The OAuth UI itself is
// exercised by hand and by the bootstrap script.
//
// The seed pattern mirrors what the real login completion does
// (`src/state/AuthProvider.tsx`):
//   1. Write per-instance OAuth client credentials to the keychain. Reused
//      when a second account is on the same host.
//   2. Write per-account access + refresh tokens to the keychain.
//   3. Upsert the registry entry in AccountStore. The registry merges on
//      `(instance, userId)` so re-seeding the same rig is idempotent.
//
// The integration tier re-exports `seedAccountFromRig` / `seedAndActivate` /
// `seedAllRigsAndActivate` from `__tests__/integration/_setup/seedAccount.ts`
// so the logic lives in exactly one place. The Maestro tier reaches the same
// functions through `seedFromRigPayload`, called by the `__DEV__`-only
// `__dev_seed` deep-link handler (`src/components/DevSeedListener.tsx`).

import { AccountStore, type AccountType } from 'src/state/accountStore'
import { secureStore } from 'src/state/storage'

/** The minimal slice of a rig entry the seed reads. The integration tier's
 *  richer `RigAccount` (which adds `slot`, `accountId`, `scopes`, …) is
 *  structurally assignable to this, so the same functions serve both tiers. */
export type RigSeedEntry = {
  type: AccountType
  instance: string
  userId: string
  username: string
  acct: string
  displayName: string
  avatarUrl: string | null
  oauthClientId: string
  oauthClientSecret: string
  accessToken: string
  refreshToken: string | null
}

/**
 * Seed the registry + keychain from a single rig entry. Returns the resulting
 * `AccountSummary` so the caller can read the canonical `accountId` (any
 * `accountId` on the input is advisory — `AccountStore.upsert` mints its own
 * UUIDv4; re-seeding the same (instance, userId) returns the same row).
 */
export function seedAccountFromRig(entry: RigSeedEntry) {
  // 1. Per-instance OAuth credentials — keyed on instance, reused by every
  //    account on that host.
  secureStore.setClientId(entry.instance, entry.oauthClientId)
  secureStore.setClientSecret(entry.instance, entry.oauthClientSecret)

  // 2. Upsert the registry row FIRST so we know the canonical accountId.
  const summary = AccountStore.upsert({
    type: entry.type,
    instance: entry.instance,
    userId: entry.userId,
    username: entry.username,
    acct: entry.acct,
    displayName: entry.displayName,
    avatarUrl: entry.avatarUrl,
    // Token-expiry is rig-bound (Mastodon: non-expiring; Pixelfed: 1y). We
    // don't model expiry in tests — pre-emptive refresh is gated on
    // `tokenExpiresAt != null && remaining < 10min`, so null skips it.
    tokenExpiresAt: null,
  })

  // 3. Per-account secrets keyed under the *registry's* accountId, so
  //    `secureStore.getAccessToken(summary.accountId)` finds them. Seeding the
  //    refresh token (when present) is what lets the app's production
  //    401→refresh self-heal recover a stale access token on the first request.
  secureStore.setAccessToken(summary.accountId, entry.accessToken)
  if (entry.refreshToken) {
    secureStore.setRefreshToken(summary.accountId, entry.refreshToken)
  }

  return summary
}

/** Seed + activate in one step — the common shape for single-account tests. */
export function seedAndActivate(entry: RigSeedEntry) {
  const summary = seedAccountFromRig(entry)
  AccountStore.setActive(summary.accountId)
  return summary
}

/** Seed every entry in the rig and activate the first one matching `defaultType`
 *  (or the first slot if `defaultType` is omitted). Used by multi-account tests
 *  and the E2E account-switch flow. */
export function seedAllRigsAndActivate(
  rigs: Record<string, RigSeedEntry>,
  defaultType?: AccountType
) {
  const seeded: Record<string, ReturnType<typeof seedAccountFromRig>> = {}
  for (const [slot, entry] of Object.entries(rigs)) {
    seeded[slot] = seedAccountFromRig(entry)
  }
  const slots = Object.keys(seeded)
  if (slots.length === 0) throw new Error('seedAllRigsAndActivate: rig has no entries')
  const firstSlot = defaultType
    ? (Object.entries(rigs).find(([, e]) => e.type === defaultType)?.[0] ?? slots[0])
    : slots[0]
  AccountStore.setActive(seeded[firstSlot].accountId)
  return seeded
}

type RigPayload = { rigs?: Record<string, RigSeedEntry> }

/**
 * Parse a rig JSON string and seed every entry, activating the first. The
 * payload is delivered as a small file on the device (written by
 * `scripts/dev/run-e2e.sh`, read by the `__dev_seed` deep-link handler) rather
 * than inline in the deep-link URL: iOS truncates `openURL` payloads at ~2 KB
 * and a single Pixelfed JWT access+refresh pair already exceeds that.
 *
 * `__DEV__`-gated: a release build cannot reach this code path. The deep-link
 * handler that calls it is *also* `__DEV__`-gated, so this is belt-and-braces.
 */
export function seedFromRigJson(json: string): {
  activeAccountId: string
  count: number
} {
  if (!__DEV__) {
    throw new Error(
      'seedFromRigJson is a dev-only seam and must not run in a release build'
    )
  }
  const payload = JSON.parse(json) as RigPayload
  const rigs = payload.rigs ?? {}
  const seeded = seedAllRigsAndActivate(rigs)
  const activeAccountId = AccountStore.activeIdOrThrow()
  return { activeAccountId, count: Object.keys(seeded).length }
}
