import { Request, Response, NextFunction } from 'express';
import { AuthService } from '../services/auth.service';
import { z } from 'zod';

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

const registerSchema = loginSchema.extend({
  role: z.enum(['ADMIN', 'PARTNER_USER']).optional(),
  organizationId: z.string().uuid().optional(),
});

export class AuthController {
  private authService = new AuthService();

  public register = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const data = registerSchema.parse(req.body);
      const user = await this.authService.register(data);
      res.status(201).json({ message: 'User registered successfully', user });
    } catch(err) { next(err); }
  }

  public invitePartner = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { email, organizationId, partnerGroup, assignedNdaId, assignedMsaId, sharedAssetIds, mfaEnabled, showEcosystemTab } = z.object({
        email: z.string().email(),
        organizationId: z.string().uuid().optional(),
        partnerGroup: z.string().optional().nullable(),
        assignedNdaId: z.string().optional().nullable(),
        assignedMsaId: z.string().optional().nullable(),
        sharedAssetIds: z.array(z.string().uuid()).optional(),
        mfaEnabled: z.boolean().optional(),
        showEcosystemTab: z.boolean().optional(),
      }).parse(req.body);

      const result = await this.authService.invitePartner(email, {
        organizationId,
        partnerGroup: partnerGroup || undefined,
        assignedNdaId: assignedNdaId || undefined,
        assignedMsaId: assignedMsaId || undefined,
        sharedAssetIds,
        mfaEnabled,
        showEcosystemTab,
      });
      res.status(201).json({ message: 'Invite created', token: result.inviteToken });
    } catch(err) { next(err); }
  }

  public updatePartner = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { partnerId } = req.params;
      const { partnerGroup, assignedNdaId, assignedMsaId, sharedAssetIds, mfaEnabled, showEcosystemTab } = z.object({
        partnerGroup: z.string().optional().nullable(),
        assignedNdaId: z.string().optional().nullable(),
        assignedMsaId: z.string().optional().nullable(),
        sharedAssetIds: z.array(z.string().uuid()).optional(),
        mfaEnabled: z.boolean().optional(),
        showEcosystemTab: z.boolean().optional(),
      }).parse(req.body);

      const result = await this.authService.updatePartner(partnerId, {
        partnerGroup: (partnerGroup === null || partnerGroup === '') ? null : partnerGroup,
        assignedNdaId: assignedNdaId === undefined ? undefined : assignedNdaId,
        assignedMsaId: assignedMsaId === undefined ? undefined : assignedMsaId,
        sharedAssetIds,
        mfaEnabled,
        showEcosystemTab,
      });
      res.status(200).json(result);
    } catch(err) { next(err); }
  }

  public resendInvite = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { partnerId } = req.params;
      const result = await this.authService.resendInvite(partnerId);
      res.status(200).json(result);
    } catch(err) { next(err); }
  }

  public validateInvite = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { token } = req.params;
      const result = await this.authService.validateInvite(token);
      res.status(200).json(result);
    } catch(err) { next(err); }
  }

  public acceptInvite = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { token, password } = z.object({ token: z.string(), password: z.string().min(6) }).parse(req.body);
      const result = await this.authService.acceptInvite(token, password);
      
      res.cookie('refreshToken', result.refreshToken, {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'lax',
        maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
      });

      res.status(200).json({
        user: result.user,
        accessToken: result.accessToken,
      });
    } catch(err) { next(err); }
  }

  public login = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const { email, password } = loginSchema.parse(req.body);
      const result = await this.authService.login(email, password);
      
      res.cookie('refreshToken', result.refreshToken, {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'lax',
        maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
      });

      res.status(200).json({
        user: result.user,
        accessToken: result.accessToken,
      });
    } catch (err) { next(err); }
  };

  public refresh = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const token = req.cookies.refreshToken;
      if (!token) {
        return res.status(401).json({ error: 'No refresh token provided' });
      }
      const result = await this.authService.refresh(token);
      res.status(200).json(result);
    } catch (err) { next(err); }
  };
  public listPartners = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const partners = await this.authService.listPartners();
      res.status(200).json(partners);
    } catch(err) { next(err); }
  };

  public getCurrentUser = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const userId = (req as any).user?.userId;
      if (!userId) {
        return res.status(401).json({ error: 'Unauthorized' });
      }
      const user = await this.authService.getUserById(userId);
      if (!user) {
        return res.status(404).json({ error: 'User not found' });
      }
      res.status(200).json(user);
    } catch(err) { next(err); }
  };

  public updateProfile = async (req: Request, res: Response, next: NextFunction) => {
    try {
      const userId = (req as any).user?.userId;
      if (!userId) {
        return res.status(401).json({ error: 'Unauthorized' });
      }
      const { password, companyName, website, sector, companySize, defaultTheme } = req.body;
      const updatedUser = await this.authService.updateProfile(userId, {
        password,
        companyName,
        website,
        sector,
        companySize,
        defaultTheme
      });
      res.status(200).json(updatedUser);
    } catch (err) { next(err); }
  };
}

