import { db, schema } from './db';
import { eq, and, count, inArray } from 'drizzle-orm';
import type { Company, CompanyFinancialData } from './schema';
import { getPlanCapabilities } from '@/lib/plan-access';

type CompanyPaymentMethodValue = (typeof schema.companies.$inferInsert)['paymentMethod'];

/**
 * Package configuration — max users and included conversion slots per tier.
 */
// 9999 = sentinel for "unlimited" users (Enterprise tier per pricing doc)
export const PACKAGE_CONFIG = {
  starter: { maxUsers: 5, maxConversions: 5, name: 'Starter' },
  professional: { maxUsers: 25, maxConversions: 15, name: 'Professional' },
  enterprise: { maxUsers: 9999, maxConversions: 40, name: 'Enterprise' },
} as const;

export const UNLIMITED_USERS = 9999;

export type PackageTier = keyof typeof PACKAGE_CONFIG;

/**
 * Get a company by ID
 */
export async function getCompanyById(companyId: string): Promise<Company | null> {
  const company = await db.query.companies.findFirst({
    where: eq(schema.companies.id, companyId),
  });
  return company || null;
}

/**
 * Get company by owner ID
 */
export async function getCompanyByOwnerId(ownerId: string): Promise<Company | null> {
  const company = await db.query.companies.findFirst({
    where: eq(schema.companies.ownerId, ownerId),
  });
  return company || null;
}

/**
 * Check if a user is the owner of a company
 */
export async function isCompanyOwner(userId: string, companyId: string): Promise<boolean> {
  const company = await getCompanyById(companyId);
  if (!company) return false;
  return company.ownerId === userId;
}

/**
 * Get all users belonging to a company
 */
export async function getCompanyUsers(companyId: string) {
  const users = await db.query.users.findMany({
    where: eq(schema.users.companyId, companyId),
    orderBy: (users, { desc }) => [desc(users.createdAt)],
  });
  return users;
}

/**
 * Count users in a company
 */
export async function countCompanyUsers(companyId: string): Promise<number> {
  const result = await db
    .select({ count: count() })
    .from(schema.users)
    .where(eq(schema.users.companyId, companyId));
  return result[0]?.count || 0;
}

/**
 * Check if company has reached its user limit
 */
export async function hasReachedUserLimit(companyId: string): Promise<boolean> {
  const company = await getCompanyById(companyId);
  if (!company) return true;
  const userCount = await countCompanyUsers(companyId);
  return userCount >= company.maxUsers;
}

/**
 * Get remaining user slots for a company
 */
export async function getRemainingUserSlots(companyId: string): Promise<number> {
  const company = await getCompanyById(companyId);
  if (!company) return 0;
  const userCount = await countCompanyUsers(companyId);
  return Math.max(0, company.maxUsers - userCount);
}

/**
 * Get the owner of a company
 */
export async function getCompanyOwner(companyId: string) {
  const company = await getCompanyById(companyId);
  if (!company) return null;

  const owner = await db.query.users.findFirst({
    where: and(
      eq(schema.users.id, company.ownerId),
      eq(schema.users.companyId, companyId)
    ),
  });
  return owner;
}

/**
 * Update company financial data (owner only operation)
 */
export async function updateCompanyFinancialData(
  companyId: string,
  financialData: Partial<CompanyFinancialData>
): Promise<Company | null> {
  const company = await getCompanyById(companyId);
  if (!company) return null;

  const updatedFinancialData = {
    ...company.financialData,
    ...financialData,
  };

  const [updated] = await db
    .update(schema.companies)
    .set({
      financialData: updatedFinancialData,
      updatedAt: new Date(),
    })
    .where(eq(schema.companies.id, companyId))
    .returning();

  return updated || null;
}

/**
 * Update company payment method (owner only operation)
 */
export async function updateCompanyPaymentMethod(
  companyId: string,
  paymentMethod: CompanyPaymentMethodValue
): Promise<Company | null> {
  const company = await getCompanyById(companyId);
  if (!company) return null;

  const [updated] = await db
    .update(schema.companies)
    .set({
      paymentMethod,
      updatedAt: new Date(),
    })
    .where(eq(schema.companies.id, companyId))
    .returning();

  return updated || null;
}

/**
 * Change company owner
 * - Verifies new owner belongs to the company
 * - Updates both company.ownerId and user.isOwner flags
 */
export async function changeCompanyOwner(
  companyId: string,
  currentOwnerId: string,
  newOwnerId: string
): Promise<{ success: boolean; error?: string }> {
  // Verify current user is the owner
  const isOwner = await isCompanyOwner(currentOwnerId, companyId);
  if (!isOwner) {
    return { success: false, error: 'Only the current owner can transfer ownership' };
  }

  // Verify new owner exists and belongs to this company
  const newOwner = await db.query.users.findFirst({
    where: and(
      eq(schema.users.id, newOwnerId),
      eq(schema.users.companyId, companyId)
    ),
  });

  if (!newOwner) {
    return { success: false, error: 'New owner must be an existing user in this company' };
  }

  if (newOwnerId === currentOwnerId) {
    return { success: false, error: 'New owner cannot be the same as current owner' };
  }

  // Perform the ownership transfer in a database transaction
  try {
    await db.transaction(async (tx) => {
      // Update current owner
      await tx
        .update(schema.users)
        .set({ isOwner: false })
        .where(eq(schema.users.id, currentOwnerId));

      // Update new owner
      await tx
        .update(schema.users)
        .set({ isOwner: true })
        .where(eq(schema.users.id, newOwnerId));

      // Update company
      await tx
        .update(schema.companies)
        .set({
          ownerId: newOwnerId,
          updatedAt: new Date(),
        })
        .where(eq(schema.companies.id, companyId));
    });

    return { success: true };
  } catch (error) {
    console.error('Error changing company owner:', error);
    return { success: false, error: 'Failed to transfer ownership' };
  }
}

/**
 * Get max users for a package tier
 */
export function getMaxUsersForPackage(packageTier: PackageTier): number {
  return PACKAGE_CONFIG[packageTier].maxUsers;
}

/**
 * Included conversion slots for a package tier (before extra purchased slots).
 */
export function getMaxConversionsForPackage(packageTier: PackageTier): number {
  return PACKAGE_CONFIG[packageTier].maxConversions;
}

/** @deprecated Use getMaxConversionsForPackage */
export const getMaxProjectsForPackage = getMaxConversionsForPackage;

/**
 * Count conversions (projects) belonging to users of a specific company.
 */
export async function countCompanyConversions(companyId: string): Promise<number> {
  const companyUsers = await db.query.users.findMany({
    where: eq(schema.users.companyId, companyId),
    columns: { id: true },
  });

  if (companyUsers.length === 0) return 0;

  const userIds = companyUsers.map(u => u.id);
  const result = await db
    .select({ count: count() })
    .from(schema.projects)
    .where(inArray(schema.projects.userId, userIds));
  return result[0]?.count ?? 0;
}

/** @deprecated Use countCompanyConversions */
export const countCompanyProjects = countCompanyConversions;

/**
 * Get all conversions for company users (DB table: projects).
 */
export async function getCompanyProjects(companyId: string) {
  const companyUsers = await db.query.users.findMany({
    where: eq(schema.users.companyId, companyId),
    columns: { id: true },
  });

  if (companyUsers.length === 0) return [];

  const userIds = companyUsers.map(u => u.id);
  return db.query.projects.findMany({
    where: inArray(schema.projects.userId, userIds),
    orderBy: (projects, { desc }) => [desc(projects.createdAt)],
  });
}

/**
 * Get the effective max conversions for a company (plan base + purchased extra slots)
 */
export function getEffectiveMaxConversions(company: { package: string; extraConversionSlots?: number | null }): number {
  const base = getMaxConversionsForPackage(company.package as PackageTier);
  return base + (company.extraConversionSlots ?? 0);
}

export function initialLicenseCreditsForPackage(packageTier: string): number {
  return getPlanCapabilities(packageTier).includedCreditsTokens;
}

/**
 * Reset plan credits to the current package allowance (manual / admin use).
 */
export async function resetCompanyLicenseCredits(companyId: string): Promise<void> {
  const company = await getCompanyById(companyId);
  if (!company) return;
  const credits = initialLicenseCreditsForPackage(company.package);
  await db
    .update(schema.companies)
    .set({ licenseCreditsRemaining: credits, updatedAt: new Date() })
    .where(eq(schema.companies.id, companyId));
}

/**
 * License credits are an ANNUAL allowance. They renew once per year on the
 * company's license anniversary (the month it was created), NOT every billing
 * cycle. Monthly billing calls this; it only resets when `asOf` falls in the
 * anniversary month, otherwise credits carry over month to month.
 */
export async function maybeResetAnnualLicenseCredits(companyId: string, asOf: Date): Promise<void> {
  const company = await getCompanyById(companyId);
  if (!company) return;
  const anniversaryMonth = company.createdAt ? new Date(company.createdAt).getMonth() : 0;
  if (asOf.getMonth() !== anniversaryMonth) return;
  const credits = initialLicenseCreditsForPackage(company.package);
  await db
    .update(schema.companies)
    .set({ licenseCreditsRemaining: credits, updatedAt: new Date() })
    .where(eq(schema.companies.id, companyId));
}

/**
 * Suspend a company (e.g. failed monthly charge / no payment method).
 * Blocks all members until the outstanding invoice is settled.
 */
export async function suspendCompany(companyId: string, reason: string): Promise<void> {
  await db
    .update(schema.companies)
    .set({ suspendedAt: new Date(), suspendedReason: reason, updatedAt: new Date() })
    .where(eq(schema.companies.id, companyId));
}

/** Lift a company suspension (e.g. after a successful payment). */
export async function reactivateCompany(companyId: string): Promise<void> {
  await db
    .update(schema.companies)
    .set({ suspendedAt: null, suspendedReason: null, updatedAt: new Date() })
    .where(eq(schema.companies.id, companyId));
}

/** True when the company is currently suspended for billing reasons. */
export async function isCompanySuspended(companyId: string): Promise<boolean> {
  const company = await getCompanyById(companyId);
  return Boolean(company?.suspendedAt);
}

/**
 * Validate if a new conversion can be created in a company (slot cap).
 */
export async function canCreateConversionInCompany(companyId: string): Promise<{ allowed: boolean; reason?: string }> {
  const company = await getCompanyById(companyId);
  if (!company) {
    return { allowed: false, reason: 'Company not found' };
  }

  if (company.suspendedAt) {
    return { allowed: false, reason: company.suspendedReason ?? 'Company account is suspended due to an unpaid invoice.' };
  }

  const conversionCount = await countCompanyConversions(companyId);
  const maxConversions = getEffectiveMaxConversions(company);

  if (conversionCount >= maxConversions) {
    return { allowed: false, reason: `Company has reached the maximum of ${maxConversions} conversions for the ${company.package} plan` };
  }

  return { allowed: true };
}

/** @deprecated Use canCreateConversionInCompany */
export const canCreateProjectInCompany = canCreateConversionInCompany;

/**
 * Validate if a user can be added to a company
 */
export async function canAddUserToCompany(companyId: string): Promise<{ allowed: boolean; reason?: string }> {
  const company = await getCompanyById(companyId);
  if (!company) {
    return { allowed: false, reason: 'Company not found' };
  }

  const userCount = await countCompanyUsers(companyId);
  if (userCount >= company.maxUsers) {
    return { allowed: false, reason: `Company has reached the maximum of ${company.maxUsers} users for the ${company.package} plan` };
  }

  return { allowed: true };
}

/**
 * Get company with all its users (for owner view)
 */
export async function getCompanyWithUsers(companyId: string) {
  const company = await getCompanyById(companyId);
  if (!company) return null;

  const users = await getCompanyUsers(companyId);
  const owner = users.find(u => u.isOwner) || null;

  return {
    ...company,
    users,
    owner,
  };
}

/**
 * Get company summary for non-owner users (limited data)
 */
export async function getCompanySummary(companyId: string) {
  const company = await getCompanyById(companyId);
  if (!company) return null;

  return {
    id: company.id,
    name: company.name,
    package: company.package,
    maxUsers: company.maxUsers,
    userCount: await countCompanyUsers(companyId),
    createdAt: company.createdAt,
    // Note: financial data, payment method, and ownedTokens are NOT included
  };
}

/**
 * Get company's owned tokens (owner only operation)
 */
export async function getCompanyTokens(companyId: string): Promise<number> {
  const company = await getCompanyById(companyId);
  if (!company) return 0;
  return company.ownedTokens;
}

/**
 * Add tokens to a company's pool (admin only operation)
 */
export async function addTokensToCompany(
  companyId: string,
  tokensToAdd: number
): Promise<{ success: boolean; newBalance?: number; error?: string }> {
  if (tokensToAdd <= 0) {
    return { success: false, error: 'Token amount must be positive' };
  }

  const company = await getCompanyById(companyId);
  if (!company) {
    return { success: false, error: 'Company not found' };
  }

  try {
    const newBalance = company.ownedTokens + tokensToAdd;
    await db
      .update(schema.companies)
      .set({
        ownedTokens: newBalance,
        updatedAt: new Date(),
      })
      .where(eq(schema.companies.id, companyId));

    return { success: true, newBalance };
  } catch (error) {
    console.error('Error adding tokens to company:', error);
    return { success: false, error: 'Failed to add tokens' };
  }
}

