import { describe, test, expect, beforeEach } from 'bun:test'
import {
  storeReset,
  storeCreateUser,
  storeGetUser,
  storeCreateToken,
  storeGetUserByToken,
  storeDeleteToken,
  storeCreateEnvironment,
  storeGetEnvironment,
  storeUpdateEnvironment,
  storeListActiveEnvironments,
  storeListActiveEnvironmentsByUsername,
  storeCreateSession,
  storeGetSession,
  storeUpdateSession,
  storeListSessions,
  storeListSessionsByUsername,
  storeListSessionsByEnvironment,
  storeDeleteSession,
  storeBindSession,
  storeIsSessionOwner,
  storeListSessionsByOwnerUuid,
  storeCreateWorkItem,
  storeGetWorkItem,
  storeGetPendingWorkItem,
  storeUpdateWorkItem,
} from '../store'

describe('store', () => {
  beforeEach(() => {
    storeReset()
  })

  // ---------- User ----------

  describe('storeCreateUser', () => {
    test('creates a new user', () => {
      const user = storeCreateUser('alice')
      expect(user.username).toBe('alice')
      expect(user.createdAt).toBeInstanceOf(Date)
    })

    test('returns existing user on duplicate create', () => {
      const first = storeCreateUser('bob')
      const second = storeCreateUser('bob')
      expect(first).toBe(second)
    })
  })

  describe('storeGetUser', () => {
    test('returns undefined for non-existent user', () => {
      expect(storeGetUser('nobody')).toBeUndefined()
    })

    test('returns created user', () => {
      storeCreateUser('charlie')
      const user = storeGetUser('charlie')
      expect(user?.username).toBe('charlie')
    })
  })

  // ---------- Token ----------

  describe('storeCreateToken / storeGetUserByToken', () => {
    test('creates and resolves token', () => {
      storeCreateUser('dave')
      storeCreateToken('dave', 'tk_123')
      const entry = storeGetUserByToken('tk_123')
      expect(entry?.username).toBe('dave')
      expect(entry?.createdAt).toBeInstanceOf(Date)
    })

    test('returns undefined for unknown token', () => {
      expect(storeGetUserByToken('nonexistent')).toBeUndefined()
    })
  })

  describe('storeDeleteToken', () => {
    test('deletes an existing token', () => {
      storeCreateUser('eve')
      storeCreateToken('eve', 'tk_del')
      expect(storeDeleteToken('tk_del')).toBe(true)
      expect(storeGetUserByToken('tk_del')).toBeUndefined()
    })

    test('returns false for non-existent token', () => {
      expect(storeDeleteToken('nope')).toBe(false)
    })
  })

  // ---------- Environment ----------

  describe('storeCreateEnvironment', () => {
    test('creates environment with defaults', () => {
      const env = storeCreateEnvironment({ secret: 's1' })
      expect(env.id).toMatch(/^env_/)
      expect(env.secret).toBe('s1')
      expect(env.status).toBe('active')
      expect(env.machineName).toBeNull()
      expect(env.maxSessions).toBe(1)
      expect(env.workerType).toBe('claude_code')
      expect(env.lastPollAt).toBeInstanceOf(Date)
    })

    test('creates environment with all options', () => {
      const env = storeCreateEnvironment({
        secret: 's2',
        machineName: 'mac1',
        directory: '/home/user',
        branch: 'main',
        gitRepoUrl: 'https://github.com/test/repo',
        maxSessions: 5,
        workerType: 'custom',
        bridgeId: 'bridge1',
        username: 'alice',
      })
      expect(env.machineName).toBe('mac1')
      expect(env.directory).toBe('/home/user')
      expect(env.branch).toBe('main')
      expect(env.gitRepoUrl).toBe('https://github.com/test/repo')
      expect(env.maxSessions).toBe(5)
      expect(env.workerType).toBe('custom')
      expect(env.bridgeId).toBe('bridge1')
      expect(env.username).toBe('alice')
    })
  })

  describe('storeGetEnvironment', () => {
    test('returns undefined for non-existent env', () => {
      expect(storeGetEnvironment('env_no')).toBeUndefined()
    })

    test('returns created environment', () => {
      const env = storeCreateEnvironment({ secret: 's' })
      expect(storeGetEnvironment(env.id)).toBe(env)
    })
  })

  describe('storeUpdateEnvironment', () => {
    test('updates existing environment', () => {
      const env = storeCreateEnvironment({ secret: 's' })
      const result = storeUpdateEnvironment(env.id, { status: 'disconnected' })
      expect(result).toBe(true)
      const updated = storeGetEnvironment(env.id)
      expect(updated?.status).toBe('disconnected')
      expect(updated?.updatedAt.getTime()).toBeGreaterThanOrEqual(
        env.updatedAt.getTime(),
      )
    })

    test('returns false for non-existent environment', () => {
      expect(storeUpdateEnvironment('env_no', { status: 'active' })).toBe(false)
    })
  })

  describe('storeListActiveEnvironments', () => {
    test('returns only active environments', () => {
      const env1 = storeCreateEnvironment({ secret: 's1' })
      const env2 = storeCreateEnvironment({ secret: 's2' })
      storeUpdateEnvironment(env1.id, { status: 'deregistered' })
      const active = storeListActiveEnvironments()
      expect(active).toHaveLength(1)
      expect(active[0].id).toBe(env2.id)
    })
  })

  describe('storeListActiveEnvironmentsByUsername', () => {
    test('filters by username', () => {
      storeCreateEnvironment({ secret: 's1', username: 'alice' })
      storeCreateEnvironment({ secret: 's2', username: 'bob' })
      const aliceEnvs = storeListActiveEnvironmentsByUsername('alice')
      expect(aliceEnvs).toHaveLength(1)
      expect(aliceEnvs[0].username).toBe('alice')
    })
  })

  // ---------- Session ----------

  describe('storeCreateSession', () => {
    test('creates session with defaults', () => {
      const session = storeCreateSession({})
      expect(session.id).toMatch(/^session_/)
      expect(session.status).toBe('idle')
      expect(session.source).toBe('remote-control')
      expect(session.environmentId).toBeNull()
      expect(session.workerEpoch).toBe(0)
    })

    test('creates session with options', () => {
      const env = storeCreateEnvironment({ secret: 's' })
      const session = storeCreateSession({
        environmentId: env.id,
        title: 'Test Session',
        source: 'cli',
        permissionMode: 'auto',
        username: 'alice',
      })
      expect(session.environmentId).toBe(env.id)
      expect(session.title).toBe('Test Session')
      expect(session.source).toBe('cli')
      expect(session.permissionMode).toBe('auto')
      expect(session.username).toBe('alice')
    })

    test('creates session with custom idPrefix', () => {
      const session = storeCreateSession({ idPrefix: 'cse_' })
      expect(session.id).toMatch(/^cse_/)
    })
  })

  describe('storeGetSession', () => {
    test('returns undefined for non-existent session', () => {
      expect(storeGetSession('nope')).toBeUndefined()
    })
  })

  describe('storeUpdateSession', () => {
    test('updates existing session', () => {
      const session = storeCreateSession({})
      const result = storeUpdateSession(session.id, {
        title: 'Updated',
        status: 'active',
      })
      expect(result).toBe(true)
      const updated = storeGetSession(session.id)
      expect(updated?.title).toBe('Updated')
      expect(updated?.status).toBe('active')
    })

    test('returns false for non-existent session', () => {
      expect(storeUpdateSession('nope', { title: 'x' })).toBe(false)
    })

    test('increments workerEpoch', () => {
      const session = storeCreateSession({})
      storeUpdateSession(session.id, { workerEpoch: 1 })
      expect(storeGetSession(session.id)?.workerEpoch).toBe(1)
    })
  })

  describe('storeListSessions', () => {
    test('returns all sessions', () => {
      storeCreateSession({})
      storeCreateSession({})
      expect(storeListSessions()).toHaveLength(2)
    })
  })

  describe('storeListSessionsByUsername', () => {
    test('filters by username', () => {
      storeCreateSession({ username: 'alice' })
      storeCreateSession({ username: 'bob' })
      expect(storeListSessionsByUsername('alice')).toHaveLength(1)
    })
  })

  describe('storeListSessionsByEnvironment', () => {
    test('filters by environment', () => {
      const env = storeCreateEnvironment({ secret: 's' })
      storeCreateSession({ environmentId: env.id })
      storeCreateSession({})
      expect(storeListSessionsByEnvironment(env.id)).toHaveLength(1)
    })
  })

  describe('storeDeleteSession', () => {
    test('deletes existing session', () => {
      const session = storeCreateSession({})
      expect(storeDeleteSession(session.id)).toBe(true)
      expect(storeGetSession(session.id)).toBeUndefined()
    })

    test('returns false for non-existent session', () => {
      expect(storeDeleteSession('nope')).toBe(false)
    })
  })

  // ---------- Session Ownership ----------

  describe('storeBindSession / storeIsSessionOwner', () => {
    test('binds and checks ownership', () => {
      const session = storeCreateSession({})
      storeBindSession(session.id, 'uuid-1')
      expect(storeIsSessionOwner(session.id, 'uuid-1')).toBe(true)
      expect(storeIsSessionOwner(session.id, 'uuid-2')).toBe(false)
    })

    test('unbound session has no owner', () => {
      const session = storeCreateSession({})
      expect(storeIsSessionOwner(session.id, 'uuid-1')).toBe(false)
    })

    test('multiple owners per session', () => {
      const session = storeCreateSession({})
      storeBindSession(session.id, 'uuid-1')
      storeBindSession(session.id, 'uuid-2')
      expect(storeIsSessionOwner(session.id, 'uuid-1')).toBe(true)
      expect(storeIsSessionOwner(session.id, 'uuid-2')).toBe(true)
    })
  })

  describe('storeListSessionsByOwnerUuid', () => {
    test('returns sessions owned by uuid', () => {
      const s1 = storeCreateSession({})
      const s2 = storeCreateSession({})
      storeBindSession(s1.id, 'uuid-1')
      storeBindSession(s2.id, 'uuid-1')
      const owned = storeListSessionsByOwnerUuid('uuid-1')
      expect(owned).toHaveLength(2)
    })

    test('returns empty for unknown uuid', () => {
      expect(storeListSessionsByOwnerUuid('nope')).toHaveLength(0)
    })

    test('excludes deleted sessions', () => {
      const s1 = storeCreateSession({})
      storeBindSession(s1.id, 'uuid-1')
      storeDeleteSession(s1.id)
      expect(storeListSessionsByOwnerUuid('uuid-1')).toHaveLength(0)
    })
  })

  // ---------- Work Items ----------

  describe('storeCreateWorkItem', () => {
    test('creates work item with defaults', () => {
      const item = storeCreateWorkItem({
        environmentId: 'env1',
        sessionId: 'ses1',
        secret: 'sec1',
      })
      expect(item.id).toMatch(/^work_/)
      expect(item.environmentId).toBe('env1')
      expect(item.sessionId).toBe('ses1')
      expect(item.state).toBe('pending')
      expect(item.secret).toBe('sec1')
    })
  })

  describe('storeGetWorkItem', () => {
    test('returns undefined for non-existent', () => {
      expect(storeGetWorkItem('nope')).toBeUndefined()
    })

    test('returns created work item', () => {
      const item = storeCreateWorkItem({
        environmentId: 'env1',
        sessionId: 'ses1',
        secret: 's',
      })
      expect(storeGetWorkItem(item.id)).toBe(item)
    })
  })

  describe('storeGetPendingWorkItem', () => {
    test('returns pending work for environment', () => {
      const item = storeCreateWorkItem({
        environmentId: 'env1',
        sessionId: 'ses1',
        secret: 's',
      })
      const found = storeGetPendingWorkItem('env1')
      expect(found?.id).toBe(item.id)
    })

    test('returns undefined when no pending work', () => {
      storeCreateWorkItem({
        environmentId: 'env1',
        sessionId: 'ses1',
        secret: 's',
      })
      expect(storeGetPendingWorkItem('env2')).toBeUndefined()
    })

    test('skips non-pending items', () => {
      const item = storeCreateWorkItem({
        environmentId: 'env1',
        sessionId: 'ses1',
        secret: 's',
      })
      storeUpdateWorkItem(item.id, { state: 'dispatched' })
      expect(storeGetPendingWorkItem('env1')).toBeUndefined()
    })
  })

  describe('storeUpdateWorkItem', () => {
    test('updates existing work item', () => {
      const item = storeCreateWorkItem({
        environmentId: 'env1',
        sessionId: 'ses1',
        secret: 's',
      })
      expect(storeUpdateWorkItem(item.id, { state: 'acked' })).toBe(true)
      expect(storeGetWorkItem(item.id)?.state).toBe('acked')
    })

    test('returns false for non-existent', () => {
      expect(storeUpdateWorkItem('nope', { state: 'acked' })).toBe(false)
    })
  })

  // ---------- storeReset ----------

  describe('storeReset', () => {
    test('clears all data', () => {
      storeCreateUser('alice')
      storeCreateEnvironment({ secret: 's' })
      storeCreateSession({})
      storeCreateWorkItem({
        environmentId: 'env1',
        sessionId: 'ses1',
        secret: 's',
      })

      storeReset()

      expect(storeGetUser('alice')).toBeUndefined()
      expect(storeListActiveEnvironments()).toHaveLength(0)
      expect(storeListSessions()).toHaveLength(0)
      expect(storeGetPendingWorkItem('env1')).toBeUndefined()
    })
  })
})
