import { FastifyPluginAsync } from 'fastify';
import { z } from 'zod';
import { db } from '../config/database.js';
import { users } from '../db/schema.js';
import { eq } from 'drizzle-orm';
import { GiteaService } from '../services/gitea.service.js';
import { env } from '../config/env.js';

const callbackQueryJsonSchema = {
  type: 'object',
  required: ['code', 'state'],
  properties: {
    code: { type: 'string' },
    state: { type: 'string' },
  },
} as const;

const authRoute: FastifyPluginAsync = async (fastify) => {
  const giteaService = new GiteaService();

  /**
   * GET /auth/gitea
   * Initiate OAuth flow
   */
  fastify.get('/auth/gitea', async (request, reply) => {
    // Generate CSRF state token
    const state = giteaService.generateState();

    // Store state in a short-lived cookie
    reply.setCookie('oauth_state', state, {
      path: '/',
      httpOnly: true,
      sameSite: 'none',
      secure: true,
      maxAge: 10 * 60, // 10 minutes
    });

    // Generate authorization URL
    const authUrl = giteaService.getAuthorizationUrl(state);

    // Redirect to Gitea
    return reply.redirect(authUrl);
  });

  /**
   * GET /auth/callback
   * OAuth callback endpoint
   */
  fastify.get('/auth/callback', {
    schema: {
      querystring: callbackQueryJsonSchema,
    },
  }, async (request, reply) => {
    try {
      const { code, state } = request.query as { code: string; state: string };

      // Verify CSRF state from cookie
      const expectedState = request.cookies?.oauth_state as string | undefined;
      if (!expectedState || state !== expectedState) {
          return reply.code(400).send({ error: 'Invalid state parameter' });
      }

      // Clear state cookie
      reply.clearCookie('oauth_state', { path: '/' });

      // Exchange code for access token
      const tokenResponse = await giteaService.exchangeCodeForToken(code);

      // Fetch user info from Gitea
      const giteaUser = await giteaService.getUserInfo(tokenResponse.access_token);

      // Check if user is allowed
      if (!giteaService.isUserAllowed(giteaUser.login)) {
        return reply.code(403).send({
          error: 'Access denied. You are not authorized to access this CMS.'
        });
      }

      // Find or create user in database
      let [user] = await db
        .select()
        .from(users)
        .where(eq(users.giteaId, giteaUser.id.toString()))
        .limit(1);

      if (!user) {
        // Create new user
        [user] = await db.insert(users).values({
          giteaId: giteaUser.id.toString(),
          giteaUsername: giteaUser.login,
          giteaEmail: giteaUser.email,
          displayName: giteaUser.full_name,
          avatarUrl: giteaUser.avatar_url,
          lastLogin: new Date(),
        }).returning();
      } else {
        // Update existing user
        [user] = await db
          .update(users)
          .set({
            giteaUsername: giteaUser.login,
            giteaEmail: giteaUser.email,
            displayName: giteaUser.full_name,
            avatarUrl: giteaUser.avatar_url,
            lastLogin: new Date(),
          })
          .where(eq(users.id, user.id))
          .returning();
      }

      // Generate JWT for session management
      const token = fastify.jwt.sign(
        {
          id: user.id,
          giteaId: user.giteaId,
          username: user.giteaUsername || '',
          role: user.role ?? 'admin',
        },
        { expiresIn: '24h' }
      );

      // Also set token as HttpOnly cookie so subsequent API calls authenticate reliably
      // Cross-site admin (gallus-pub.ch) -> backend (cms.gallus-pub.ch) requires SameSite=None & Secure in production
      reply.setCookie('token', token, {
        path: '/',
        httpOnly: true,
        sameSite: (env.NODE_ENV === 'production' ? 'none' : 'lax'),
        secure: (env.NODE_ENV === 'production') || (!!env.FRONTEND_URL && env.FRONTEND_URL.startsWith('https')),
        maxAge: 60 * 60 * 24, // 24h
      });

      // Redirect to admin dashboard
      const frontendUrl = env.FRONTEND_URL;
      return reply.redirect(`${frontendUrl}/admin`);

    } catch (error) {
      fastify.log.error({ err: error }, 'OAuth callback error');
      return reply.code(500).send({ error: 'Authentication failed' });
    }
  });

  /**
   * GET /auth/me
   * Get current user info
   */
  fastify.get('/auth/me', {
    preHandler: [fastify.authenticate],
  }, async (request, reply) => {
    const userId = request.user.id;

    const [user] = await db
      .select()
      .from(users)
      .where(eq(users.id, userId))
      .limit(1);

    if (!user) {
      return reply.code(404).send({ error: 'User not found' });
    }

    return {
      user: {
        id: user.id,
        giteaUsername: user.giteaUsername,
        giteaEmail: user.giteaEmail,
        displayName: user.displayName,
        avatarUrl: user.avatarUrl,
        role: user.role,
      },
    };
  });

  /**
   * POST /auth/logout
   * Logout (client-side token deletion)
   */
  fastify.post('/auth/logout', {
    preHandler: [fastify.authenticate],
  }, async (request, reply) => {
    // For JWT, logout is primarily client-side (delete token)
    // You could maintain a token blacklist in Redis for production
    reply.clearCookie('token', { path: '/' });
    return { message: 'Logged out successfully' };
  });
};

export default authRoute;
