import { randomUUID } from "crypto";
import type { Permission } from "@/lib/constants/auth";
import { prisma } from "@/lib/prisma";
import {
  decryptServiceToken,
  encryptServiceToken,
  generateServiceToken,
  hashServiceToken,
  tokenKeyPrefix,
} from "@/modules/customer/token-crypto";
import {
  DEFAULT_EMBED_PASSWORD,
  hashEmbedPassword,
  validateEmbedPassword,
  compareEmbedPassword,
} from "@/modules/customer/embed-password";
import { getEmbedDemoPassword } from "@/modules/embed-demo/auth";
import { isKnownCustomerAsync } from "@/modules/auth/service-token";
import type {
  CreateHostCustomerResult,
  HostCustomerDto,
  HostCustomerStatus,
  ServiceApiKeySummaryDto,
} from "@/modules/customer/types";

const DEFAULT_PERMISSIONS: Permission[] = ["pricing:markup:write"];

function parsePermissions(value: unknown): Permission[] {
  if (!Array.isArray(value)) {
    return DEFAULT_PERMISSIONS;
  }
  return value.filter(
    (item): item is Permission => typeof item === "string",
  );
}

function serializeApiKeySummary(row: {
  keyId: string;
  keyPrefix: string;
  isActive: boolean;
  permissions: unknown;
  createdAt: Date;
}): ServiceApiKeySummaryDto {
  return {
    key_id: row.keyId,
    key_prefix: row.keyPrefix,
    is_active: row.isActive,
    permissions: parsePermissions(row.permissions),
    created_at: row.createdAt.toISOString(),
  };
}

function serializeCustomer(row: {
  customerId: string;
  name: string;
  status: string;
  remark: string | null;
  embedPasswordHash?: string | null;
  activeApiKey?: string | null;
  createdAt: Date;
  updatedAt: Date;
  apiKeys: Array<{
    keyId: string;
    keyPrefix: string;
    isActive: boolean;
    permissions: unknown;
    createdAt: Date;
  }>;
}): HostCustomerDto {
  return {
    customer_id: row.customerId,
    name: row.name,
    status: row.status as HostCustomerStatus,
    remark: row.remark,
    embed_password_set: Boolean(row.embedPasswordHash),
    active_api_key: row.activeApiKey ?? null,
    created_at: row.createdAt.toISOString(),
    updated_at: row.updatedAt.toISOString(),
    api_keys: row.apiKeys.map(serializeApiKeySummary),
  };
}

async function resolveEmbedPasswordHash(
  password?: string,
): Promise<string> {
  const raw = password?.trim() || DEFAULT_EMBED_PASSWORD;
  const error = validateEmbedPassword(raw);
  if (error) {
    throw new Error(error);
  }
  return hashEmbedPassword(raw);
}

async function nextCustomerId(): Promise<string> {
  const rows = await prisma.hostCustomer.findMany({
    where: { customerId: { startsWith: "CUST_" } },
    select: { customerId: true },
  });
  let max = 0;
  for (const row of rows) {
    const n = Number.parseInt(row.customerId.replace(/^CUST_/, ""), 10);
    if (Number.isFinite(n) && n > max) {
      max = n;
    }
  }
  return `CUST_${String(max + 1).padStart(3, "0")}`;
}

async function createApiKeyRecord(
  customerId: string,
): Promise<{ rawToken: string; summary: ServiceApiKeySummaryDto }> {
  const rawToken = generateServiceToken();
  const created = await prisma.serviceApiKey.create({
    data: {
      keyId: `KEY_${randomUUID().replace(/-/g, "").slice(0, 12).toUpperCase()}`,
      customerId,
      tokenHash: hashServiceToken(rawToken),
      tokenCiphertext: encryptServiceToken(rawToken),
      keyPrefix: tokenKeyPrefix(rawToken),
      permissions: DEFAULT_PERMISSIONS,
      isActive: true,
    },
  });
  return {
    rawToken,
    summary: serializeApiKeySummary(created),
  };
}

export async function listHostCustomers(options: {
  page: number;
  size: number;
  keyword?: string;
}): Promise<{
  list: HostCustomerDto[];
  total: number;
  page: number;
  size: number;
}> {
  const keyword = options.keyword?.trim();
  const where = {
    isDeleted: false,
    ...(keyword
      ? {
          OR: [
            { customerId: { contains: keyword } },
            { name: { contains: keyword } },
          ],
        }
      : {}),
  };

  const [total, rows] = await Promise.all([
    prisma.hostCustomer.count({ where }),
    prisma.hostCustomer.findMany({
      where,
      include: {
        apiKeys: {
          where: { isActive: true },
          orderBy: { createdAt: "desc" },
        },
      },
      orderBy: { createdAt: "desc" },
      skip: (options.page - 1) * options.size,
      take: options.size,
    }),
  ]);

  return {
    list: rows.map((row) => {
      const activeKey = row.apiKeys.find((item) => item.isActive);
      return serializeCustomer({
        ...row,
        activeApiKey: decryptServiceToken(activeKey?.tokenCiphertext),
      });
    }),
    total,
    page: options.page,
    size: options.size,
  };
}

export async function createHostCustomer(input: {
  name: string;
  remark?: string | null;
  embed_password?: string;
}): Promise<CreateHostCustomerResult> {
  const name = input.name.trim();
  if (!name) {
    throw new Error("客户名称不能为空");
  }

  const customerId = await nextCustomerId();
  const embedPasswordHash = await resolveEmbedPasswordHash(input.embed_password);
  await prisma.hostCustomer.create({
    data: {
      customerId,
      name,
      remark: input.remark?.trim() || null,
      status: "active",
      embedPasswordHash,
    },
  });

  const { rawToken } = await createApiKeyRecord(customerId);
  const refreshed = await prisma.hostCustomer.findFirstOrThrow({
    where: { customerId },
    include: { apiKeys: { orderBy: { createdAt: "desc" } } },
  });

  return {
    ...serializeCustomer({
      ...refreshed,
      activeApiKey: rawToken,
    }),
    api_key: rawToken,
  };
}

export async function updateHostCustomer(
  customerId: string,
  input: {
    name?: string;
    status?: HostCustomerStatus;
    remark?: string | null;
    embed_password?: string;
  },
): Promise<HostCustomerDto> {
  const existing = await prisma.hostCustomer.findFirst({
    where: { customerId, isDeleted: false },
  });
  if (!existing) {
    throw new Error("客户不存在");
  }

  const data: {
    name?: string;
    status?: string;
    remark?: string | null;
    embedPasswordHash?: string;
  } = {};
  if (input.name !== undefined) {
    const name = input.name.trim();
    if (!name) {
      throw new Error("客户名称不能为空");
    }
    data.name = name;
  }
  if (input.status !== undefined) {
    data.status = input.status;
  }
  if (input.remark !== undefined) {
    data.remark = input.remark?.trim() || null;
  }
  if (input.embed_password !== undefined) {
    const error = validateEmbedPassword(input.embed_password);
    if (error) {
      throw new Error(error);
    }
    data.embedPasswordHash = await hashEmbedPassword(input.embed_password);
  }

  const updated = await prisma.hostCustomer.update({
    where: { customerId },
    data,
    include: {
      apiKeys: {
        orderBy: { createdAt: "desc" },
      },
    },
  });

  const activeKey = updated.apiKeys.find((item) => item.isActive);
  return serializeCustomer({
    ...updated,
    activeApiKey: decryptServiceToken(activeKey?.tokenCiphertext),
  });
}

export async function rotateHostCustomerApiKey(
  customerId: string,
): Promise<CreateHostCustomerResult> {
  const customer = await prisma.hostCustomer.findFirst({
    where: { customerId, isDeleted: false },
    include: { apiKeys: { orderBy: { createdAt: "desc" } } },
  });
  if (!customer) {
    throw new Error("客户不存在");
  }
  if (customer.status !== "active") {
    throw new Error("客户已停用，无法轮换密钥");
  }

  await prisma.serviceApiKey.updateMany({
    where: { customerId, isActive: true },
    data: { isActive: false },
  });

  const { rawToken } = await createApiKeyRecord(customerId);
  const refreshed = await prisma.hostCustomer.findFirstOrThrow({
    where: { customerId },
    include: { apiKeys: { orderBy: { createdAt: "desc" } } },
  });

  return {
    ...serializeCustomer({
      ...refreshed,
      activeApiKey: rawToken,
    }),
    api_key: rawToken,
  };
}

export async function listActiveDbCustomerIds(): Promise<string[]> {
  const rows = await prisma.hostCustomer.findMany({
    where: { isDeleted: false, status: "active" },
    select: { customerId: true },
    orderBy: { customerId: "asc" },
  });
  return rows.map((row) => row.customerId);
}

export async function isDbCustomerActive(customerId: string): Promise<boolean> {
  const row = await prisma.hostCustomer.findFirst({
    where: { customerId, isDeleted: false, status: "active" },
    select: { customerId: true },
  });
  return Boolean(row);
}

export async function verifyCustomerEmbedPassword(
  customerId: string,
  password: string,
): Promise<boolean> {
  const row = await prisma.hostCustomer.findFirst({
    where: { customerId, isDeleted: false, status: "active" },
    select: { embedPasswordHash: true },
  });

  if (row?.embedPasswordHash) {
    return compareEmbedPassword(password, row.embedPasswordHash);
  }

  if (row) {
    return password === getEmbedDemoPassword();
  }

  if (await isKnownCustomerAsync(customerId)) {
    return password === getEmbedDemoPassword();
  }

  return false;
}

export async function resolveDbServiceToken(
  rawToken: string,
): Promise<{ customerId: string; permissions: Permission[] } | null> {
  const tokenHash = hashServiceToken(rawToken);
  const row = await prisma.serviceApiKey.findFirst({
    where: {
      tokenHash,
      isActive: true,
      customer: { isDeleted: false, status: "active" },
    },
    include: { customer: true },
  });
  if (!row) {
    return null;
  }
  return {
    customerId: row.customerId,
    permissions: parsePermissions(row.permissions),
  };
}
