import type { Request, Response, NextFunction } from 'express';
import * as inviteModel from '../models/organizationInvite.model.js';
import * as authModel from '../models/auth.model.js';
import { firebaseAuth } from '../config/firebase.js';
import { signInWithPassword, FirebaseRestError } from '../utils/firebaseIdentity.js';
import { successResponse, errorResponse } from '../utils/response.js';
import logger from '../config/logger.js';

function isFirebaseAdminError(error: unknown): error is { code: string; message: string } {
  return typeof error === 'object' && error !== null && 'code' in error;
}

async function getInvite(req: Request, res: Response, next: NextFunction) {
  try {
    const invite = await inviteModel.findInviteByToken(String(req.params.token));
    if (!invite) return void errorResponse(res, 'Invite not found', 404);
    if (invite.status !== 'pending') return void errorResponse(res, `This invite has already been ${invite.status}`, 409);
    if (invite.expiresAt < new Date()) return void errorResponse(res, 'This invite has expired', 410);
    return successResponse(res, 'Invite fetched successfully', {
      email: invite.email,
      name: invite.name,
      organizationName: invite.organization.name,
      expiresAt: invite.expiresAt,
    });
  } catch (error) {
    next(error);
    return;
  }
}

// The invited person sets a password here, becoming the tenant's
// Organization Admin — mirrors auth.controller.ts's signup(), but the
// local User row is created with the invite's organizationId/role instead
// of the global signup defaults.
async function acceptInvite(req: Request, res: Response, next: NextFunction) {
  try {
    const invite = await inviteModel.findInviteByToken(String(req.params.token));
    if (!invite) return void errorResponse(res, 'Invite not found', 404);
    if (invite.status !== 'pending') return void errorResponse(res, `This invite has already been ${invite.status}`, 409);
    if (invite.expiresAt < new Date()) return void errorResponse(res, 'This invite has expired', 410);

    const { password } = req.body;
    const existing = await authModel.findUserByEmail(invite.email);
    if (existing) return void errorResponse(res, 'Email is already registered', 409);

    let firebaseUser;
    try {
      firebaseUser = await firebaseAuth.createUser({ email: invite.email, password });
    } catch (error) {
      if (isFirebaseAdminError(error) && error.code === 'auth/email-already-exists') {
        return void errorResponse(res, 'Email is already registered', 409);
      }
      throw error;
    }

    const user = await inviteModel.acceptOrganizationInvite(invite, firebaseUser.uid, invite.name);

    let session;
    try {
      session = await signInWithPassword(invite.email, password);
    } catch (error) {
      if (error instanceof FirebaseRestError) {
        logger.error('Invite accepted but auto sign-in failed', { email: invite.email, code: error.code });
        return successResponse(res, 'Invite accepted. Please log in.', {
          user: { id: user.id, name: user.name, email: user.email, role: user.role?.name ?? 'organization-admin' },
        }, 201);
      }
      throw error;
    }

    res.set('X-Id-Token', session.idToken);
    res.set('X-Refresh-Token', session.refreshToken);
    return successResponse(res, 'Invite accepted successfully', {
      user: { id: user.id, name: user.name, email: user.email, role: user.role?.name ?? 'organization-admin' },
    }, 201);
  } catch (error) {
    next(error);
    return;
  }
}

export { getInvite, acceptInvite };
