/**
 * Seed the two initial users and their organization memberships.
 *
 * Run from /srv/apps/work after .env is filled in and the schema migration
 * has been applied in the Supabase SQL editor:
 *
 *   npm install
 *   npm run seed
 *
 * Prints the generated temporary passwords ONCE to stdout. Capture them
 * from your terminal — they aren't stored anywhere.
 *
 * Idempotent: re-running it updates roles/profile flags but won't touch
 * passwords for users that already exist.
 */

import { config as loadEnv } from 'dotenv'
import { createClient } from '@supabase/supabase-js'
import { randomBytes } from 'node:crypto'

loadEnv()

const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL
const SECRET_KEY = process.env.SUPABASE_SECRET_KEY

if (!SUPABASE_URL || SUPABASE_URL.includes('REPLACE_WITH')) {
  console.error('NEXT_PUBLIC_SUPABASE_URL is not set in .env')
  process.exit(1)
}
if (!SECRET_KEY || SECRET_KEY.includes('REPLACE_WITH')) {
  console.error('SUPABASE_SECRET_KEY is not set in .env')
  process.exit(1)
}

const admin = createClient(SUPABASE_URL, SECRET_KEY, {
  auth: { autoRefreshToken: false, persistSession: false },
})

type SeedUser = {
  email: string
  full_name: string
  is_super_admin: boolean
}

type SeedOrg = {
  name: string
  slug: string
}

type SeedMembership = {
  email: string
  org_slug: string
  role: 'owner' | 'manager' | 'member'
}

const USERS: SeedUser[] = [
  { email: 'rian@rian.ca', full_name: 'Rian', is_super_admin: true },
  { email: 'info@adipramono.com', full_name: 'Adi', is_super_admin: false },
]

const ORGS: SeedOrg[] = [
  { name: 'Bowden Works', slug: 'bowden-works' },
  { name: 'Tingang', slug: 'tingang' },
]

const MEMBERSHIPS: SeedMembership[] = [
  { email: 'rian@rian.ca', org_slug: 'bowden-works', role: 'owner' },
  { email: 'info@adipramono.com', org_slug: 'bowden-works', role: 'manager' },
  { email: 'info@adipramono.com', org_slug: 'tingang', role: 'owner' },
]

function generateTempPassword(): string {
  // 18 url-safe chars, plenty of entropy for a one-time temp password.
  return randomBytes(14).toString('base64url')
}

async function findUserByEmail(email: string) {
  // listUsers is paginated; for a 2-user seed one page is fine.
  const { data, error } = await admin.auth.admin.listUsers({
    page: 1,
    perPage: 200,
  })
  if (error) throw error
  return data.users.find((u) => u.email?.toLowerCase() === email.toLowerCase())
}

async function upsertUser(user: SeedUser): Promise<{
  id: string
  tempPassword: string | null
}> {
  const existing = await findUserByEmail(user.email)

  if (existing) {
    console.log(`  user exists: ${user.email}  (id=${existing.id})`)
    return { id: existing.id, tempPassword: null }
  }

  const tempPassword = generateTempPassword()
  const { data, error } = await admin.auth.admin.createUser({
    email: user.email,
    password: tempPassword,
    email_confirm: true,
    user_metadata: { full_name: user.full_name },
  })
  if (error || !data.user) {
    throw error ?? new Error(`createUser returned no user for ${user.email}`)
  }
  console.log(`  user created: ${user.email}  (id=${data.user.id})`)
  return { id: data.user.id, tempPassword }
}

async function upsertProfile(
  userId: string,
  user: SeedUser,
  isNewUser: boolean,
) {
  const payload = {
    id: userId,
    email: user.email,
    full_name: user.full_name,
    is_super_admin: user.is_super_admin,
    // Only force a password change for users we just created.
    ...(isNewUser ? { must_change_password: true } : {}),
  }
  const { error } = await admin.from('profiles').upsert(payload, {
    onConflict: 'id',
  })
  if (error) throw error
}

async function upsertOrg(org: SeedOrg): Promise<string> {
  const { data: existing, error: selectErr } = await admin
    .from('organizations')
    .select('id')
    .eq('slug', org.slug)
    .maybeSingle()
  if (selectErr) throw selectErr

  if (existing) {
    console.log(`  org exists:   ${org.name}`)
    return existing.id
  }

  const { data, error } = await admin
    .from('organizations')
    .insert({ name: org.name, slug: org.slug })
    .select('id')
    .single()
  if (error || !data) throw error ?? new Error('insert org returned no row')
  console.log(`  org created:  ${org.name}`)
  return data.id
}

async function upsertMembership(
  orgId: string,
  userId: string,
  role: SeedMembership['role'],
) {
  const { error } = await admin.from('organization_members').upsert(
    {
      organization_id: orgId,
      user_id: userId,
      role,
    },
    { onConflict: 'organization_id,user_id' },
  )
  if (error) throw error
}

async function main() {
  console.log('Seeding users...')
  const userIds = new Map<string, string>()
  const tempPasswords = new Map<string, string>()

  for (const u of USERS) {
    const { id, tempPassword } = await upsertUser(u)
    userIds.set(u.email, id)
    await upsertProfile(id, u, tempPassword !== null)
    if (tempPassword) tempPasswords.set(u.email, tempPassword)
  }

  console.log('\nSeeding organizations...')
  const orgIds = new Map<string, string>()
  for (const o of ORGS) {
    orgIds.set(o.slug, await upsertOrg(o))
  }

  console.log('\nSeeding memberships...')
  for (const m of MEMBERSHIPS) {
    const userId = userIds.get(m.email)
    const orgId = orgIds.get(m.org_slug)
    if (!userId || !orgId) {
      throw new Error(`missing id for ${m.email} or ${m.org_slug}`)
    }
    await upsertMembership(orgId, userId, m.role)
    console.log(`  ${m.email}  ->  ${m.org_slug}  (${m.role})`)
  }

  if (tempPasswords.size > 0) {
    console.log('\n' + '='.repeat(60))
    console.log('TEMPORARY PASSWORDS — copy these now, they will not be shown again:')
    console.log('='.repeat(60))
    for (const [email, pw] of tempPasswords) {
      console.log(`  ${email.padEnd(28)}  ${pw}`)
    }
    console.log('='.repeat(60))
    console.log('Each user will be forced to set a new password on first login.\n')
  } else {
    console.log('\nNo new users — all already existed. No temporary passwords generated.')
  }
}

main().catch((err) => {
  console.error('\nSeed failed:', err)
  process.exit(1)
})
