import { Router, Request, Response } from 'express';
import { z } from 'zod';
import db from '../db';
import {
  hashPassword,
  comparePassword,
  generateToken,
  validateEmail,
  validatePassword,
  generatePasswordResetToken,
  verifyPasswordResetToken,
  generateEmailVerificationToken,
  verifyEmailVerificationToken,
} from '../utils/auth';
import { sendPasswordResetEmail, sendEmailVerification } from '../services/alerter';

const router = Router();

const registerSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});

const loginSchema = z.object({
  email: z.string().email(),
  password: z.string(),
});

// Register
router.post('/register', async (req: Request, res: Response): Promise<void> => {
  try {
    const { email, password } = registerSchema.parse(req.body);

    if (!validateEmail(email)) {
      res.status(400).json({
        error: 'invalid_email',
        message: 'Invalid email format',
      });
      return;
    }

    const passwordValidation = validatePassword(password);
    if (!passwordValidation.valid) {
      res.status(400).json({
        error: 'invalid_password',
        message: 'Password does not meet requirements',
        details: passwordValidation.errors,
      });
      return;
    }

    const existingUser = await db.users.findByEmail(email);
    if (existingUser) {
      res.status(409).json({
        error: 'user_exists',
        message: 'User with this email already exists',
      });
      return;
    }

    const passwordHash = await hashPassword(password);
    const user = await db.users.create(email, passwordHash);

    // Generate verification token and send email
    const verificationToken = generateEmailVerificationToken(email);
    const verificationUrl = `${process.env.APP_URL || 'http://localhost:3000'}/verify-email/${verificationToken}`;

    try {
      await sendEmailVerification(email, verificationUrl);
    } catch (emailError) {
      console.error('Failed to send verification email:', emailError);
      // Continue with registration even if email fails
    }

    const token = generateToken(user);

    res.status(201).json({
      token,
      user: {
        id: user.id,
        email: user.email,
        plan: user.plan,
        emailVerified: user.emailVerified || false,
        createdAt: user.createdAt,
      },
    });
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({
        error: 'validation_error',
        message: 'Invalid input',
        details: error.errors,
      });
      return;
    }

    console.error('Register error:', error);
    res.status(500).json({
      error: 'server_error',
      message: 'Failed to register user',
    });
  }
});

// Login
router.post('/login', async (req: Request, res: Response): Promise<void> => {
  try {
    const { email, password } = loginSchema.parse(req.body);

    const user = await db.users.findByEmail(email);
    if (!user) {
      res.status(401).json({
        error: 'invalid_credentials',
        message: 'Invalid email or password',
      });
      return;
    }

    const isValidPassword = await comparePassword(password, user.passwordHash);
    if (!isValidPassword) {
      res.status(401).json({
        error: 'invalid_credentials',
        message: 'Invalid email or password',
      });
      return;
    }

    await db.users.updateLastLogin(user.id);

    const token = generateToken(user);

    res.json({
      token,
      user: {
        id: user.id,
        email: user.email,
        plan: user.plan,
        createdAt: user.createdAt,
        lastLoginAt: new Date(),
      },
    });
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({
        error: 'validation_error',
        message: 'Invalid input',
        details: error.errors,
      });
      return;
    }

    console.error('Login error:', error);
    res.status(500).json({
      error: 'server_error',
      message: 'Failed to login',
    });
  }
});

// Forgot Password
router.post('/forgot-password', async (req: Request, res: Response): Promise<void> => {
  try {
    const { email } = z.object({ email: z.string().email() }).parse(req.body);

    const user = await db.users.findByEmail(email);

    // Always return success to prevent email enumeration attacks
    if (!user) {
      res.json({ message: 'If that email is registered, you will receive a password reset link' });
      return;
    }

    // Generate reset token
    const resetToken = generatePasswordResetToken(email);

    // Send reset email
    const resetUrl = `${process.env.APP_URL || 'http://localhost:3000'}/reset-password/${resetToken}`;

    try {
      await sendPasswordResetEmail(user.email, resetUrl);
    } catch (emailError) {
      console.error('Failed to send password reset email:', emailError);
      // Still return success to user, but log the error
    }

    res.json({ message: 'If that email is registered, you will receive a password reset link' });
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({
        error: 'validation_error',
        message: 'Invalid email',
        details: error.errors,
      });
      return;
    }

    console.error('Forgot password error:', error);
    res.status(500).json({
      error: 'server_error',
      message: 'Failed to process password reset request',
    });
  }
});

// Reset Password
router.post('/reset-password', async (req: Request, res: Response): Promise<void> => {
  try {
    const { token, newPassword } = z.object({
      token: z.string(),
      newPassword: z.string().min(8),
    }).parse(req.body);

    // Verify token
    let email: string;
    try {
      const decoded = verifyPasswordResetToken(token);
      email = decoded.email;
    } catch (error) {
      res.status(400).json({
        error: 'invalid_token',
        message: 'Invalid or expired reset token',
      });
      return;
    }

    // Validate new password
    const passwordValidation = validatePassword(newPassword);
    if (!passwordValidation.valid) {
      res.status(400).json({
        error: 'invalid_password',
        message: 'Password does not meet requirements',
        details: passwordValidation.errors,
      });
      return;
    }

    // Find user
    const user = await db.users.findByEmail(email);
    if (!user) {
      res.status(404).json({
        error: 'user_not_found',
        message: 'User not found',
      });
      return;
    }

    // Update password
    const newPasswordHash = await hashPassword(newPassword);
    await db.users.updatePassword(user.id, newPasswordHash);

    res.json({ message: 'Password reset successfully' });
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({
        error: 'validation_error',
        message: 'Invalid input',
        details: error.errors,
      });
      return;
    }

    console.error('Reset password error:', error);
    res.status(500).json({
      error: 'server_error',
      message: 'Failed to reset password',
    });
  }
});

// Verify Email
router.post('/verify-email', async (req: Request, res: Response): Promise<void> => {
  try {
    const { token } = z.object({ token: z.string() }).parse(req.body);

    // Verify token
    let email: string;
    try {
      const decoded = verifyEmailVerificationToken(token);
      email = decoded.email;
    } catch (error) {
      res.status(400).json({
        error: 'invalid_token',
        message: 'Invalid or expired verification token',
      });
      return;
    }

    // Find user
    const user = await db.users.findByEmail(email);
    if (!user) {
      res.status(404).json({
        error: 'user_not_found',
        message: 'User not found',
      });
      return;
    }

    // Check if already verified
    if (user.emailVerified) {
      res.json({ message: 'Email already verified' });
      return;
    }

    // Mark email as verified
    await db.users.verifyEmail(email);

    res.json({ message: 'Email verified successfully' });
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({
        error: 'validation_error',
        message: 'Invalid input',
        details: error.errors,
      });
      return;
    }

    console.error('Verify email error:', error);
    res.status(500).json({
      error: 'server_error',
      message: 'Failed to verify email',
    });
  }
});

// Resend Verification Email
router.post('/resend-verification', async (req: Request, res: Response): Promise<void> => {
  try {
    const { email } = z.object({ email: z.string().email() }).parse(req.body);

    const user = await db.users.findByEmail(email);

    // Always return success to prevent email enumeration
    if (!user || user.emailVerified) {
      res.json({ message: 'If that email needs verification, a new link has been sent' });
      return;
    }

    // Generate new verification token
    const verificationToken = generateEmailVerificationToken(email);
    const verificationUrl = `${process.env.APP_URL || 'http://localhost:3000'}/verify-email/${verificationToken}`;

    try {
      await sendEmailVerification(email, verificationUrl);
    } catch (emailError) {
      console.error('Failed to resend verification email:', emailError);
    }

    res.json({ message: 'If that email needs verification, a new link has been sent' });
  } catch (error) {
    if (error instanceof z.ZodError) {
      res.status(400).json({
        error: 'validation_error',
        message: 'Invalid email',
        details: error.errors,
      });
      return;
    }

    console.error('Resend verification error:', error);
    res.status(500).json({
      error: 'server_error',
      message: 'Failed to resend verification email',
    });
  }
});

export default router;
