import { NextRequest, NextResponse } from 'next/server'

/**
 * SDK State Management API
 *
 * GET /api/sdk/v1/state?tenantId=xxx - Load state for a tenant
 * POST /api/sdk/v1/state - Save state for a tenant
 * DELETE /api/sdk/v1/state?tenantId=xxx - Clear state for a tenant
 *
 * Features:
 * - Versioning for optimistic locking
 * - Last-Modified headers
 * - ETag support for caching
 * - Prepared for PostgreSQL migration
 */

// =============================================================================
// TYPES
// =============================================================================

interface StoredState {
  state: unknown
  version: number
  userId?: string
  createdAt: string
  updatedAt: string
}

// =============================================================================
// STORAGE LAYER (Abstract - Easy to swap to PostgreSQL)
// =============================================================================

/**
 * In-memory storage for development
 * TODO: Replace with PostgreSQL implementation
 *
 * PostgreSQL Schema:
 * CREATE TABLE sdk_states (
 *   id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
 *   tenant_id VARCHAR(255) NOT NULL UNIQUE,
 *   user_id VARCHAR(255),
 *   state JSONB NOT NULL,
 *   version INTEGER DEFAULT 1,
 *   created_at TIMESTAMP DEFAULT NOW(),
 *   updated_at TIMESTAMP DEFAULT NOW()
 * );
 *
 * CREATE INDEX idx_sdk_states_tenant ON sdk_states(tenant_id);
 */

interface StateStore {
  get(tenantId: string): Promise<StoredState | null>
  save(tenantId: string, state: unknown, userId?: string, expectedVersion?: number): Promise<StoredState>
  delete(tenantId: string): Promise<boolean>
}

class InMemoryStateStore implements StateStore {
  private store: Map<string, StoredState> = new Map()

  async get(tenantId: string): Promise<StoredState | null> {
    return this.store.get(tenantId) || null
  }

  async save(
    tenantId: string,
    state: unknown,
    userId?: string,
    expectedVersion?: number
  ): Promise<StoredState> {
    const existing = this.store.get(tenantId)

    // Optimistic locking check
    if (expectedVersion !== undefined && existing && existing.version !== expectedVersion) {
      const error = new Error('Version conflict') as Error & { status: number }
      error.status = 409
      throw error
    }

    const now = new Date().toISOString()
    const newVersion = existing ? existing.version + 1 : 1

    const stored: StoredState = {
      state: {
        ...(state as object),
        lastModified: now,
      },
      version: newVersion,
      userId,
      createdAt: existing?.createdAt || now,
      updatedAt: now,
    }

    this.store.set(tenantId, stored)
    return stored
  }

  async delete(tenantId: string): Promise<boolean> {
    return this.store.delete(tenantId)
  }
}

// Future PostgreSQL implementation would look like:
// class PostgreSQLStateStore implements StateStore {
//   private db: Pool
//
//   constructor(connectionString: string) {
//     this.db = new Pool({ connectionString })
//   }
//
//   async get(tenantId: string): Promise<StoredState | null> {
//     const result = await this.db.query(
//       'SELECT state, version, user_id, created_at, updated_at FROM sdk_states WHERE tenant_id = $1',
//       [tenantId]
//     )
//     if (result.rows.length === 0) return null
//     const row = result.rows[0]
//     return {
//       state: row.state,
//       version: row.version,
//       userId: row.user_id,
//       createdAt: row.created_at,
//       updatedAt: row.updated_at,
//     }
//   }
//
//   async save(tenantId: string, state: unknown, userId?: string, expectedVersion?: number): Promise<StoredState> {
//     // Use UPSERT with version check
//     const result = await this.db.query(`
//       INSERT INTO sdk_states (tenant_id, user_id, state, version)
//       VALUES ($1, $2, $3, 1)
//       ON CONFLICT (tenant_id) DO UPDATE SET
//         state = $3,
//         user_id = COALESCE($2, sdk_states.user_id),
//         version = sdk_states.version + 1,
//         updated_at = NOW()
//       WHERE ($4::int IS NULL OR sdk_states.version = $4)
//       RETURNING version, created_at, updated_at
//     `, [tenantId, userId, JSON.stringify(state), expectedVersion])
//
//     if (result.rows.length === 0) {
//       throw new Error('Version conflict')
//     }
//
//     return {
//       state,
//       version: result.rows[0].version,
//       userId,
//       createdAt: result.rows[0].created_at,
//       updatedAt: result.rows[0].updated_at,
//     }
//   }
//
//   async delete(tenantId: string): Promise<boolean> {
//     const result = await this.db.query(
//       'DELETE FROM sdk_states WHERE tenant_id = $1',
//       [tenantId]
//     )
//     return result.rowCount > 0
//   }
// }

// Use in-memory store for now
const stateStore: StateStore = new InMemoryStateStore()

// =============================================================================
// HELPER FUNCTIONS
// =============================================================================

function generateETag(version: number, updatedAt: string): string {
  return `"${version}-${Buffer.from(updatedAt).toString('base64').slice(0, 8)}"`
}

// =============================================================================
// HANDLERS
// =============================================================================

export async function GET(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url)
    const tenantId = searchParams.get('tenantId')

    if (!tenantId) {
      return NextResponse.json(
        { success: false, error: 'tenantId is required' },
        { status: 400 }
      )
    }

    const stored = await stateStore.get(tenantId)

    if (!stored) {
      return NextResponse.json(
        { success: false, error: 'State not found', tenantId },
        { status: 404 }
      )
    }

    const etag = generateETag(stored.version, stored.updatedAt)

    // Check If-None-Match header for caching
    const ifNoneMatch = request.headers.get('If-None-Match')
    if (ifNoneMatch === etag) {
      return new NextResponse(null, { status: 304 })
    }

    return NextResponse.json(
      {
        success: true,
        data: {
          tenantId,
          state: stored.state,
          version: stored.version,
          lastModified: stored.updatedAt,
        },
      },
      {
        headers: {
          'ETag': etag,
          'Last-Modified': stored.updatedAt,
          'Cache-Control': 'private, no-cache',
        },
      }
    )
  } catch (error) {
    console.error('Failed to load SDK state:', error)
    return NextResponse.json(
      { success: false, error: 'Failed to load state' },
      { status: 500 }
    )
  }
}

export async function POST(request: NextRequest) {
  try {
    const body = await request.json()
    const { tenantId, state, version } = body

    if (!tenantId) {
      return NextResponse.json(
        { success: false, error: 'tenantId is required' },
        { status: 400 }
      )
    }

    if (!state) {
      return NextResponse.json(
        { success: false, error: 'state is required' },
        { status: 400 }
      )
    }

    // Check If-Match header for optimistic locking
    const ifMatch = request.headers.get('If-Match')
    const expectedVersion = ifMatch ? parseInt(ifMatch, 10) : version

    const stored = await stateStore.save(tenantId, state, body.userId, expectedVersion)

    const etag = generateETag(stored.version, stored.updatedAt)

    return NextResponse.json(
      {
        success: true,
        data: {
          tenantId,
          state: stored.state,
          version: stored.version,
          lastModified: stored.updatedAt,
        },
      },
      {
        headers: {
          'ETag': etag,
          'Last-Modified': stored.updatedAt,
        },
      }
    )
  } catch (error) {
    const err = error as Error & { status?: number }

    // Handle version conflict
    if (err.status === 409 || err.message === 'Version conflict') {
      return NextResponse.json(
        {
          success: false,
          error: 'Version conflict. State was modified by another request.',
          code: 'VERSION_CONFLICT',
        },
        { status: 409 }
      )
    }

    console.error('Failed to save SDK state:', error)
    return NextResponse.json(
      { success: false, error: 'Failed to save state' },
      { status: 500 }
    )
  }
}

export async function DELETE(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url)
    const tenantId = searchParams.get('tenantId')

    if (!tenantId) {
      return NextResponse.json(
        { success: false, error: 'tenantId is required' },
        { status: 400 }
      )
    }

    const deleted = await stateStore.delete(tenantId)

    if (!deleted) {
      return NextResponse.json(
        { success: false, error: 'State not found', tenantId },
        { status: 404 }
      )
    }

    return NextResponse.json({
      success: true,
      tenantId,
      deletedAt: new Date().toISOString(),
    })
  } catch (error) {
    console.error('Failed to delete SDK state:', error)
    return NextResponse.json(
      { success: false, error: 'Failed to delete state' },
      { status: 500 }
    )
  }
}

// =============================================================================
// HEALTH CHECK
// =============================================================================

export async function OPTIONS() {
  return NextResponse.json({ status: 'ok' }, {
    headers: {
      'Allow': 'GET, POST, DELETE, OPTIONS',
    },
  })
}
