/**
 * Multi-Tenant repository — Cosmos DB CRUD for tenants, members, and invites.
 * @module tenants/repository
 */

import { getContainer } from '../../lib/cosmos.js';
import type {
  TenantDoc,
  TenantMemberDoc,
  TenantInviteDoc,
  TenantStatus,
  MemberRole,
} from './types.js';

// =============================================================================
// Tenants
// =============================================================================

export async function createTenant(doc: TenantDoc): Promise<TenantDoc> {
  const container = getContainer('tenants');
  const { resource } = await container.items.create(doc);
  if (!resource) throw new Error('Failed to create tenant');
  return resource as unknown as TenantDoc;
}

export async function getTenant(id: string, productId: string): Promise<TenantDoc | null> {
  const container = getContainer('tenants');
  try {
    const { resource } = await container.item(id, productId).read();
    return resource as unknown as TenantDoc | null;
  } catch (err) {
    if ((err as { code?: number }).code === 404) return null;
    throw err;
  }
}

export async function getTenantBySlug(productId: string, slug: string): Promise<TenantDoc | null> {
  const container = getContainer('tenants');
  const query = 'SELECT * FROM c WHERE c.productId = @productId AND c.slug = @slug';
  const parameters = [
    { name: '@productId', value: productId },
    { name: '@slug', value: slug },
  ];
  const { resources } = await container.items.query<TenantDoc>({ query, parameters }).fetchAll();
  return resources[0] ?? null;
}

export async function updateTenant(
  id: string,
  productId: string,
  updates: Partial<TenantDoc>
): Promise<TenantDoc | null> {
  const existing = await getTenant(id, productId);
  if (!existing) return null;

  const container = getContainer('tenants');
  const updated: TenantDoc = {
    ...existing,
    ...updates,
    id: existing.id,
    productId: existing.productId,
    updatedAt: new Date().toISOString(),
  };

  const { resource } = await container.items.upsert(updated);
  if (!resource) throw new Error('Failed to update tenant');
  return resource as unknown as TenantDoc;
}

export async function listTenants(
  productId: string,
  options?: { status?: TenantStatus; ownerId?: string; limit?: number }
): Promise<{ tenants: TenantDoc[]; total: number }> {
  const container = getContainer('tenants');

  let query = 'SELECT * FROM c WHERE c.productId = @productId';
  const parameters = [{ name: '@productId', value: productId }];

  if (options?.status) {
    query += ' AND c.status = @status';
    parameters.push({ name: '@status', value: options.status });
  }
  if (options?.ownerId) {
    query += ' AND c.ownerId = @ownerId';
    parameters.push({ name: '@ownerId', value: options.ownerId });
  }

  query += ' ORDER BY c.createdAt DESC';

  const countQuery = query.replace('SELECT *', 'SELECT VALUE COUNT(1)');
  const { resources: countResult } = await container.items
    .query<number>({ query: countQuery, parameters })
    .fetchAll();
  const total = countResult[0] ?? 0;

  const safeLimit = Math.min(Math.max(options?.limit ?? 50, 1), 200);
  query += ` OFFSET 0 LIMIT ${safeLimit}`;

  const { resources } = await container.items.query<TenantDoc>({ query, parameters }).fetchAll();

  return { tenants: resources, total };
}

export async function deleteTenant(id: string, productId: string): Promise<boolean> {
  const container = getContainer('tenants');
  try {
    await container.item(id, productId).delete();
    return true;
  } catch (err) {
    if ((err as { code?: number }).code === 404) return false;
    throw err;
  }
}

// =============================================================================
// Tenant Members
// =============================================================================

export async function addMember(doc: TenantMemberDoc): Promise<TenantMemberDoc> {
  const container = getContainer('tenant_members');
  const { resource } = await container.items.create(doc);
  if (!resource) throw new Error('Failed to add tenant member');
  return resource as unknown as TenantMemberDoc;
}

export async function getMember(tenantId: string, userId: string): Promise<TenantMemberDoc | null> {
  const container = getContainer('tenant_members');
  const query = 'SELECT * FROM c WHERE c.tenantId = @tenantId AND c.userId = @userId';
  const parameters = [
    { name: '@tenantId', value: tenantId },
    { name: '@userId', value: userId },
  ];
  const { resources } = await container.items
    .query<TenantMemberDoc>({ query, parameters })
    .fetchAll();
  return resources[0] ?? null;
}

export async function listMembers(
  tenantId: string,
  options?: { role?: MemberRole; limit?: number }
): Promise<{ members: TenantMemberDoc[]; total: number }> {
  const container = getContainer('tenant_members');

  let query = 'SELECT * FROM c WHERE c.tenantId = @tenantId';
  const parameters = [{ name: '@tenantId', value: tenantId }];

  if (options?.role) {
    query += ' AND c.role = @role';
    parameters.push({ name: '@role', value: options.role });
  }

  query += ' ORDER BY c.joinedAt DESC';

  const countQuery = query.replace('SELECT *', 'SELECT VALUE COUNT(1)');
  const { resources: countResult } = await container.items
    .query<number>({ query: countQuery, parameters })
    .fetchAll();
  const total = countResult[0] ?? 0;

  const safeLimit = Math.min(Math.max(options?.limit ?? 50, 1), 200);
  query += ` OFFSET 0 LIMIT ${safeLimit}`;

  const { resources } = await container.items
    .query<TenantMemberDoc>({ query, parameters })
    .fetchAll();

  return { members: resources, total };
}

export async function updateMemberRole(
  tenantId: string,
  userId: string,
  role: MemberRole
): Promise<TenantMemberDoc | null> {
  const existing = await getMember(tenantId, userId);
  if (!existing) return null;

  const container = getContainer('tenant_members');
  const updated: TenantMemberDoc = {
    ...existing,
    role,
    updatedAt: new Date().toISOString(),
  };

  const { resource } = await container.items.upsert(updated);
  if (!resource) throw new Error('Failed to update member role');
  return resource as unknown as TenantMemberDoc;
}

export async function removeMember(memberId: string, tenantId: string): Promise<boolean> {
  const container = getContainer('tenant_members');
  try {
    await container.item(memberId, tenantId).delete();
    return true;
  } catch (err) {
    if ((err as { code?: number }).code === 404) return false;
    throw err;
  }
}

export async function getMemberCount(tenantId: string): Promise<number> {
  const container = getContainer('tenant_members');
  const query = 'SELECT VALUE COUNT(1) FROM c WHERE c.tenantId = @tenantId';
  const parameters = [{ name: '@tenantId', value: tenantId }];
  const { resources } = await container.items.query<number>({ query, parameters }).fetchAll();
  return resources[0] ?? 0;
}

// =============================================================================
// Tenant Invites
// =============================================================================

export async function createInvite(doc: TenantInviteDoc): Promise<TenantInviteDoc> {
  const container = getContainer('tenant_invites');
  const { resource } = await container.items.create(doc);
  if (!resource) throw new Error('Failed to create invite');
  return resource as unknown as TenantInviteDoc;
}

export async function getInviteByTokenHash(
  tokenHash: string,
  tenantId: string
): Promise<TenantInviteDoc | null> {
  const container = getContainer('tenant_invites');
  const query =
    'SELECT * FROM c WHERE c.tenantId = @tenantId AND c.tokenHash = @tokenHash AND c.acceptedAt = null';
  const parameters = [
    { name: '@tenantId', value: tenantId },
    { name: '@tokenHash', value: tokenHash },
  ];
  const { resources } = await container.items
    .query<TenantInviteDoc>({ query, parameters })
    .fetchAll();
  return resources[0] ?? null;
}

export async function listPendingInvites(tenantId: string): Promise<TenantInviteDoc[]> {
  const container = getContainer('tenant_invites');
  const query =
    'SELECT * FROM c WHERE c.tenantId = @tenantId AND c.acceptedAt = null ORDER BY c.createdAt DESC';
  const parameters = [{ name: '@tenantId', value: tenantId }];
  const { resources } = await container.items
    .query<TenantInviteDoc>({ query, parameters })
    .fetchAll();
  return resources;
}

export async function markInviteAccepted(
  inviteId: string,
  tenantId: string
): Promise<TenantInviteDoc | null> {
  const container = getContainer('tenant_invites');
  try {
    const { resource: existing } = await container.item(inviteId, tenantId).read();
    if (!existing) return null;
    const doc = existing as unknown as TenantInviteDoc;
    const updated = { ...doc, acceptedAt: new Date().toISOString() };
    const { resource } = await container.items.upsert(updated);
    return resource as unknown as TenantInviteDoc;
  } catch (err) {
    if ((err as { code?: number }).code === 404) return null;
    throw err;
  }
}
