import type { Request, Response, NextFunction } from 'express';
import * as organizationModel from '../models/organization.model.js';
import { successResponse, errorResponse } from '../utils/response.js';
import logger from '../config/logger.js';

function actorId(req: Request): number | undefined {
  return req.user ? Number(req.user.sub) : undefined;
}

function parsePage(query: Request['query']) {
  const page = Math.max(1, Math.trunc(Number(query.page)) || 1);
  const pageSize = Math.min(100, Math.max(1, Math.trunc(Number(query.pageSize)) || 20));
  return { page, pageSize, skip: (page - 1) * pageSize, take: pageSize };
}

async function createOrganization(req: Request, res: Response, next: NextFunction) {
  try {
    const { organization, invite } = await organizationModel.createOrganization(req.body, actorId(req));
    // No email provider is wired up yet — log the invite link so it can be
    // shared manually until one is. See INVITE_TTL_DAYS in invite.model.ts.
    logger.info('Tenant admin invite created', { organizationId: organization.id, email: invite.email, token: invite.token });
    return successResponse(res, 'Organization created successfully', organization, 201);
  } catch (error) {
    next(error);
    return;
  }
}

async function getOrganizations(req: Request, res: Response, next: NextFunction) {
  try {
    const { page, pageSize, skip, take } = parsePage(req.query);
    const filter = {
      search: typeof req.query.search === 'string' ? req.query.search : undefined,
      status: typeof req.query.status === 'string' && req.query.status ? req.query.status : undefined,
      planId: typeof req.query.planId === 'string' ? req.query.planId : undefined,
    };
    const { items, total, statusCounts } = await organizationModel.getOrganizations({ skip, take }, filter);
    return successResponse(res, 'Organizations fetched successfully', { items, total, page, pageSize, statusCounts });
  } catch (error) {
    next(error);
    return;
  }
}

async function getOrganization(req: Request, res: Response, next: NextFunction) {
  try {
    const organization = await organizationModel.getOrganizationById(Number(req.params.id));
    if (!organization) return void errorResponse(res, 'Organization not found', 404);
    return successResponse(res, 'Organization fetched successfully', organization);
  } catch (error) {
    next(error);
    return;
  }
}

async function getOrganizationUsers(req: Request, res: Response, next: NextFunction) {
  try {
    const users = await organizationModel.getOrganizationUsers(Number(req.params.id));
    return successResponse(res, 'Organization users fetched successfully', users);
  } catch (error) {
    next(error);
    return;
  }
}

async function updateOrganization(req: Request, res: Response, next: NextFunction) {
  try {
    const organization = await organizationModel.updateOrganization(Number(req.params.id), req.body);
    return successResponse(res, 'Organization updated successfully', organization);
  } catch (error) {
    next(error);
    return;
  }
}

async function updateStatus(req: Request, res: Response, next: NextFunction) {
  try {
    const organization = await organizationModel.updateStatus(Number(req.params.id), req.body, actorId(req));
    return successResponse(res, 'Organization status updated successfully', organization);
  } catch (error) {
    next(error);
    return;
  }
}

async function updateSubscription(req: Request, res: Response, next: NextFunction) {
  try {
    const organization = await organizationModel.updateSubscription(Number(req.params.id), req.body, actorId(req));
    return successResponse(res, 'Subscription updated successfully', organization);
  } catch (error) {
    next(error);
    return;
  }
}

async function changeTenantAdmin(req: Request, res: Response, next: NextFunction) {
  try {
    const result = await organizationModel.changeTenantAdmin(Number(req.params.id), req.body, actorId(req));
    if (result.mode === 'invite') {
      logger.info('Tenant admin invite created', { organizationId: Number(req.params.id), email: req.body.email, token: result.invite.token });
    }
    return successResponse(res, 'Tenant admin updated successfully', result);
  } catch (error) {
    next(error);
    return;
  }
}

async function resendTenantAdminInvite(req: Request, res: Response, next: NextFunction) {
  try {
    const result = await organizationModel.resendTenantAdminInvite(Number(req.params.id), actorId(req));
    if (!result) return void errorResponse(res, 'No invite found for this organization', 404);
    if (result === 'already-accepted') return void errorResponse(res, 'This invite has already been accepted', 409);
    logger.info('Tenant admin invite resent', { organizationId: Number(req.params.id), email: result.email, token: result.token });
    return successResponse(res, 'Invite resent successfully');
  } catch (error) {
    next(error);
    return;
  }
}

export {
  createOrganization, getOrganizations, getOrganization, getOrganizationUsers, updateOrganization,
  updateStatus, updateSubscription, changeTenantAdmin, resendTenantAdminInvite,
};
