import { Request, Response, NextFunction } from 'express';
import { AuthService } from '../services/AuthService';
import { AuthOAuthService } from '../services/AuthOAuthService';
import { UserView } from '../views/UserView';
import { env } from '../config/env';
import { isAuthOAuthProvider } from '../config/authOAuth';
import { AppError } from '../utils/AppError';

export class AuthController {
  constructor(
    private authService = new AuthService(),
    private authOAuthService = new AuthOAuthService(),
  ) {}

  register = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { user, passwordEmailed } = await this.authService.register(req.body);
      res.status(201).json(UserView.toRegisterResponse(user, passwordEmailed));
    } catch (err) {
      next(err);
    }
  };

  login = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { user, accessToken } = await this.authService.login(req.body.email, req.body.password);
      res.json({
        accessToken,
        expiresIn: env.JWT_EXPIRES_IN,
        tokenType: 'Bearer',
        user: UserView.toAuthUser(user),
      });
    } catch (err) {
      next(err);
    }
  };

  forgotPassword = async (req: Request, res: Response, next: NextFunction) => {
    try {
      await this.authService.forgotPassword(req.body.email);
      res.json({
        message:
          'If an account exists for that email, you will receive password reset instructions shortly.',
      });
    } catch (err) {
      next(err);
    }
  };

  resetPassword = async (req: Request, res: Response, next: NextFunction) => {
    try {
      await this.authService.resetPassword(req.body.token, req.body.password);
      res.json({ message: 'Password updated. You can sign in with your new password.' });
    } catch (err) {
      next(err);
    }
  };

  changePassword = async (req: Request, res: Response, next: NextFunction) => {
    try {
      await this.authService.changePassword(
        req.userId!,
        req.body.currentPassword,
        req.body.newPassword,
      );
      res.json({ message: 'Password changed successfully.' });
    } catch (err) {
      next(err);
    }
  };

  oauthStart = (req: Request, res: Response, next: NextFunction) => {
    try {
      const { provider } = req.params;
      const url = this.authOAuthService.getAuthorizeUrl(provider, req);
      res.redirect(url);
    } catch (err) {
      next(err);
    }
  };

  oauthCallback = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { provider } = req.params;
      const code = typeof req.query.code === 'string' ? req.query.code : '';
      const state = typeof req.query.state === 'string' ? req.query.state : '';
      const oauthError = typeof req.query.error === 'string' ? req.query.error : '';

      if (oauthError) {
        res.redirect(`${env.APP_URL}/auth/callback?error=${encodeURIComponent(oauthError)}`);
        return;
      }
      if (!code || !state || !isAuthOAuthProvider(provider)) {
        throw new AppError('validation_error', 400, 'Invalid OAuth callback.');
      }

      const { accessToken } = await this.authOAuthService.handleCallback(provider, code, state);
      res.redirect(`${env.APP_URL}/auth/callback?token=${encodeURIComponent(accessToken)}`);
    } catch (err) {
      const message =
        err instanceof AppError ? err.message : 'Sign-in failed. Please try again.';
      res.redirect(`${env.APP_URL}/auth/callback?error=${encodeURIComponent(message)}`);
    }
  };
}
