import { Request, Response, NextFunction } from 'express';
import { PgUserRepository } from '../../infrastructure/repositories/postgresql/user.repository';
import { AuthService } from '../../domain/services/auth.service';
import { ApiError } from '../../utils/errors/api.error';
import logger from '../../config/logger';

// Initialize repositories and services
const userRepository = new PgUserRepository();
const authService = new AuthService(userRepository);

/**
 * Authentication Controller
 * Handles user authentication and API client authentication
 */
export class AuthController {
  /**
   * User login endpoint
   * @route POST /api/auth/login
   */
  async login(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { email, password } = req.body;

      const result = await authService.authenticateUser(email, password);

      res.status(200).json({
        status: 'success',
        data: result
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Client credentials token endpoint
   * @route POST /api/auth/token
   */
  async token(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { grant_type, client_id, client_secret, scope, refresh_token } = req.body;

      // Handle different grant types
      if (grant_type === 'client_credentials') {
        if (!client_id || !client_secret) {
          throw ApiError.badRequest('client_id and client_secret are required for client_credentials grant type');
        }

        const result = await authService.authenticateClient(client_id, client_secret, scope);

        res.status(200).json({
          access_token: result.accessToken,
          token_type: 'Bearer',
          expires_in: result.expiresIn,
          scope: result.scope
        });
      } else if (grant_type === 'refresh_token') {
        if (!refresh_token) {
          throw ApiError.badRequest('refresh_token is required for refresh_token grant type');
        }

        const result = await authService.refreshToken(refresh_token);

        res.status(200).json({
          access_token: result.accessToken,
          token_type: 'Bearer',
          expires_in: result.expiresIn
        });
      } else {
        throw ApiError.badRequest('Unsupported grant_type. Must be client_credentials or refresh_token');
      }
    } catch (error) {
      logger.error('Auth token error:', error);
      next(error);
    }
  }

  /**
   * Refresh token endpoint
   * @route POST /api/auth/refresh
   */
  async refresh(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { refresh_token } = req.body;

      if (!refresh_token) {
        throw ApiError.badRequest('refresh_token is required');
      }

      const result = await authService.refreshToken(refresh_token);

      res.status(200).json({
        status: 'success',
        data: {
          accessToken: result.accessToken,
          expiresIn: result.expiresIn
        }
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * API client management endpoints
   */

  /**
   * Get all API clients
   * @route GET /api/auth/clients
   */
  async getAllClients(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const page = parseInt(req.query.page as string) || 1;
      const limit = parseInt(req.query.limit as string) || 10;

      // Extract filter parameters
      const filters: Record<string, any> = {};
      if (req.query.client_name) {
        filters.client_name = req.query.client_name;
      }
      if (req.query.active !== undefined) {
        filters.active = req.query.active === 'true';
      }

      const result = await authService.getApiClients(page, limit, filters);

      res.status(200).json({
        status: 'success',
        data: result
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Get a client by ID
   * @route GET /api/auth/clients/:id
   */
  async getClientById(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const clientId = req.params.id;

      const client = await authService.getApiClientById(clientId);

      if (!client) {
        throw ApiError.notFound(`Client with ID ${clientId} not found`);
      }

      res.status(200).json({
        status: 'success',
        data: { client }
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Create a new API client
   * @route POST /api/auth/clients
   */
  async createClient(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { client_name, client_description, allowed_scopes } = req.body;

      const client = await authService.createApiClient({
        client_name,
        client_description,
        allowed_scopes
      });

      res.status(201).json({
        status: 'success',
        data: {
          client,
          message: 'Please save the client_secret as it will not be shown again'
        }
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Update an API client
   * @route PUT /api/auth/clients/:id
   */
  async updateClient(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const clientId = req.params.id;
      const { client_name, client_description, allowed_scopes, active } = req.body;

      const client = await authService.updateApiClient(clientId, {
        client_name,
        client_description,
        allowed_scopes,
        active
      });

      if (!client) {
        throw ApiError.notFound(`Client with ID ${clientId} not found`);
      }

      res.status(200).json({
        status: 'success',
        data: { client }
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Delete an API client
   * @route DELETE /api/auth/clients/:id
   */
  async deleteClient(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const clientId = req.params.id;

      const success = await authService.deleteApiClient(clientId);

      if (!success) {
        throw ApiError.notFound(`Client with ID ${clientId} not found`);
      }

      res.status(200).json({
        status: 'success',
        message: `Client with ID ${clientId} has been deleted`
      });
    } catch (error) {
      next(error);
    }
  }

  /**
   * Regenerate client secret
   * @route POST /api/auth/clients/:id/regenerate-secret
   */
  async regenerateClientSecret(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const clientId = req.params.id;

      const clientSecret = await authService.regenerateClientSecret(clientId);

      if (!clientSecret) {
        throw ApiError.notFound(`Client with ID ${clientId} not found`);
      }

      res.status(200).json({
        status: 'success',
        data: {
          client_id: clientId,
          client_secret: clientSecret,
          message: 'Please save the new client_secret as it will not be shown again'
        }
      });
    } catch (error) {
      next(error);
    }
  }
}

// Export controller instance
export const authController = new AuthController();
