import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { trpcServer } from '@hono/trpc-server'
import { getStaffUserById, isUserSuspended } from '@/src/dbService'
import mainRouter from '@/src/main-router'
import { appRouter } from '@/src/trpc/router'
import { TRPCError } from '@trpc/server'
import { jwtVerify } from 'jose'
import { getEncodedJwtSecret } from '@/src/lib/env-exporter'

export const createApp = () => {
  const app = new Hono()

  // CORS middleware
  app.use(cors({
    origin: ['http://localhost:5174', 'http://localhost:4174', 'https://ui.freshyo.in', 'https://www.freshyo.in', 'https://webui.freshyo.in', 'https://app.freshyo.in'],
    allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
    allowHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'Authorization', 'Caller-Interface'],
    credentials: true,
  }))

  // Logger middleware
  app.use(logger())

  // tRPC middleware
  app.use('/api/trpc/*', trpcServer({
    router: appRouter,
    createContext: async ({ req, c }) => {
      let user = null
      let staffUser = null
      const authHeader = req.headers.get('authorization')
      const callerInterface = req.headers.get('caller-interface')

      let token: string | null = null

      if (authHeader?.startsWith('Bearer ')) {
        token = authHeader.substring(7)
      } else {
        // Fallback: try reading token from cookie
        const cookieHeader = req.headers.get('cookie')
        if (cookieHeader) {
          const cookies = Object.fromEntries(
            cookieHeader.split(';').map((pair) => {
              const [k, ...v] = pair.trim().split('=')
              return [k, v.join('=')]
            })
          )
          token = cookies['auth_token'] || null
        }
      }

      if (token) {
        try {
          const { payload } = await jwtVerify(token, getEncodedJwtSecret())
          const decoded = payload as any

          // Check if this is a staff token (has staffId)
          if (decoded.staffId) {
            // This is a staff token, verify staff exists
            const staff = await getStaffUserById(decoded.staffId)

            if (staff) {
              user = staffUser
              staffUser = {
                id: staff.id,
                name: staff.name,
              }
            }
          } else {
            // This is a regular user token
            user = decoded

            // Check if user is suspended
            const suspended = await isUserSuspended(user.userId)

            if (suspended) {
              throw new TRPCError({
                code: 'FORBIDDEN',
                message: 'Account suspended',
              })
            }
          }
        } catch (err) {
          // Invalid token, both user and staffUser remain null
        }
      }
      return { req, c, user, staffUser, callerInterface }
    },
    onError({ error, path, type, ctx }) {
      console.error('🚨 tRPC Error :', {
        path,
        type,
        code: error.code,
        message: error.message,
        userId: ctx?.user?.userId,
        stack: error.stack,
      })
    },
  }))

  // Mount main router
  app.route('/api', mainRouter)

  // Global error handler
  app.onError((err, c) => {
    console.error(err)
    // Handle different error types
    let status = 500
    let message = 'Internal Server Error'

    if (err instanceof TRPCError) {
      // Map TRPC error codes to HTTP status codes
      const trpcStatusMap: Record<string, number> = {
        BAD_REQUEST: 400,
        UNAUTHORIZED: 401,
        FORBIDDEN: 403,
        NOT_FOUND: 404,
        TIMEOUT: 408,
        CONFLICT: 409,
        PRECONDITION_FAILED: 412,
        PAYLOAD_TOO_LARGE: 413,
        METHOD_NOT_SUPPORTED: 405,
        UNPROCESSABLE_CONTENT: 422,
        TOO_MANY_REQUESTS: 429,
        INTERNAL_SERVER_ERROR: 500,
      }
      status = trpcStatusMap[err.code] || 500
      message = err.message
    } else if ((err as any).statusCode) {
      status = (err as any).statusCode
      message = err.message
    } else if ((err as any).status) {
      status = (err as any).status
      message = err.message
    } else if (err.message) {
      message = err.message
    }

    return c.json({ message }, status as any)
  })

  return app
}
