import type { Prisma as PrismaTypes } from "@/generated/prisma/client"
import { Prisma, asPrismaKnownError } from "@/lib/prisma"
import type { PrismaDelegateClient } from "@/lib/prisma"
import { prisma } from "@/lib/prisma"
import { ZodError } from "zod"

import {
  companyFormSchema,
  companyInputSchema,
  type CompanyCommunicationChannels,
  type CompanyFormValues,
  type CompanyStateRegistrationTypeOption,
} from "@/lib/schemas/company"

type DbCompany = Awaited<ReturnType<PrismaDelegateClient["company"]["create"]>>
type CompanyFindManyArgs = Parameters<PrismaDelegateClient["company"]["findMany"]>[0]
type CompanyCreatePayload = Parameters<PrismaDelegateClient["company"]["create"]>[0]["data"]

export type NormalizedCompany = CompanyFormValues & {
  id: string
  provisioningCode: string | null
  createdAt: string
  updatedAt: string
}

// Local representation of the DB enum to avoid relying on Prisma enum exports
type DbCompanyStateRegistrationType = "STANDARD" | "EXEMPT" | "SIMPLES"

function asDbStateRegistrationType(value: unknown): DbCompanyStateRegistrationType | undefined {
  return value === "STANDARD" || value === "EXEMPT" || value === "SIMPLES" ? value : undefined
}

function slugify(value?: string | null): string {
  if (!value) return ""
  const ascii = value
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .replace(/[^\w\s-]/g, "")
  const collapsed = ascii.trim().replace(/[_\s]+/g, "-")
  const sanitized = collapsed.replace(/-+/g, "-").toLowerCase()
  return sanitized.replace(/^-+|-+$/g, "")
}

function ensureSlugValue(
  inputSlug: string | null | undefined,
  fallbackName: string | null | undefined,
  fallbackId?: string
): string {
  const slugFromInput = slugify(inputSlug)
  if (slugFromInput) return slugFromInput
  const slugFromName = slugify(fallbackName)
  if (slugFromName) return slugFromName
  if (fallbackId) {
    const slugFromId = slugify(fallbackId)
    if (slugFromId) return slugFromId
    return fallbackId.toLowerCase()
  }
  return ""
}

const STATE_REGISTRATION_TYPE_TO_PRISMA: Record<
  CompanyStateRegistrationTypeOption,
  DbCompanyStateRegistrationType
> = {
  standard: "STANDARD",
  exempt: "EXEMPT",
  simples: "SIMPLES",
}

const STATE_REGISTRATION_TYPE_FROM_PRISMA: Record<
  DbCompanyStateRegistrationType,
  CompanyStateRegistrationTypeOption
> = {
  STANDARD: "standard",
  EXEMPT: "exempt",
  SIMPLES: "simples",
}

type PrismaJsonInput = PrismaTypes.InputJsonValue | PrismaTypes.NullableJsonNullValueInput

const JSON_NULL_VALUE: PrismaTypes.NullableJsonNullValueInput = Prisma.JsonNull

function toJsonInput(value: unknown): PrismaJsonInput {
  if (value === null || value === undefined) {
    return JSON_NULL_VALUE
  }
  if (value === Prisma.JsonNull || value === Prisma.DbNull) {
    return value
  }
  return value as PrismaTypes.InputJsonValue
}

function asJsonRecord(value: PrismaTypes.JsonValue | null | undefined): Record<string, unknown> | undefined {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    return undefined
  }
  return value as Record<string, unknown>
}

const COMPANY_LEGACY_SELECT = {
  id: true,
  tenantId: true,
  name: true,
  slug: true,
  isAvulso: true,
  createdAt: true,
  updatedAt: true,
} as const

function isMissingProvisioningCodeColumn(error: unknown): boolean {
  const knownError = asPrismaKnownError(error)
  if (knownError && knownError.code === "P2022") {
    const meta = knownError.meta as Record<string, unknown> | undefined
    const metaColumn =
      typeof meta?.column === "string"
        ? meta.column
        : typeof meta?.column_name === "string"
          ? meta.column_name
          : typeof meta?.model === "string"
            ? meta.model
            : null
    if (metaColumn && metaColumn.toLowerCase().includes("provisioningcode")) return true
    if (knownError.message?.toLowerCase().includes("provisioningcode")) return true
  }
  return false
}

function isMissingCompanyTable(error: unknown): boolean {
  const knownError = asPrismaKnownError(error)
  if (knownError) {
    if (knownError.code === "P2021") return true
    const message = knownError.message?.toLowerCase() ?? ""
    if (message.includes("table") && message.includes("company") && message.includes("does not exist")) {
      return true
    }
  }
  return false
}

export async function safeCompanyFindMany(args: CompanyFindManyArgs = {} as CompanyFindManyArgs): Promise<DbCompany[]> {
  try {
    return await prisma.company.findMany(args)
  } catch (error) {
    if (isMissingCompanyTable(error)) {
      return []
    }
    if (!isMissingProvisioningCodeColumn(error)) {
      throw error
    }

    const hasProjection = Boolean(args?.select || args?.include)
    if (hasProjection) {
      throw error
    }

    const legacyRows = await prisma.company.findMany({
      ...args,
      select: COMPANY_LEGACY_SELECT,
    })

    return legacyRows.map(
      (row) => ({
          id: row.id,
          tenantId: row.tenantId,
          name: row.name,
          slug: row.slug,
          provisioningCode: "",
          isAvulso: Boolean(row.isAvulso),
          contractedHoursPerMonth: null,
          cnpj: null,
          domain: null,
          phone: null,
          description: null,
          address: null,
          legalName: null,
          tradeName: null,
          stateRegistration: null,
          stateRegistrationType: null,
          primaryCnae: null,
          timezone: null,
          businessHours: null,
          supportEmail: null,
          billingEmail: null,
          contactPreferences: null,
          clientDomains: null,
          communicationChannels: null,
          fiscalAddress: null,
          hasBranches: false,
          regulatedEnvironments: null,
          privacyPolicyAccepted: false,
          privacyPolicyReference: null,
          privacyPolicyMetadata: null,
          contacts: null,
          locations: null,
          contracts: null,
          sla: null,
          tags: null,
          customFields: null,
          notes: null,
          createdAt: row.createdAt,
          updatedAt: row.updatedAt,
        }) as DbCompany
    )
  }
}

export function formatZodError(error: ZodError) {
  return error.issues.map((issue) => ({
    path: issue.path.join("."),
    message: issue.message,
  }))
}

function sanitizeDomain(value?: string | null) {
  if (!value) return null
  const trimmed = value.trim().toLowerCase()
  return trimmed.length === 0 ? null : trimmed
}

function sanitizePhone(value?: string | null) {
  if (!value) return null
  const trimmed = value.trim()
  return trimmed.length === 0 ? null : trimmed
}

function normalizeChannels(
  channels?: Partial<CompanyCommunicationChannels> | null
): CompanyCommunicationChannels {
  const ensure = (values?: string[]) =>
    Array.from(new Set((values ?? []).map((value) => value.trim()).filter(Boolean)))
  return {
    supportEmails: ensure(channels?.supportEmails).map((email) => email.toLowerCase()),
    billingEmails: ensure(channels?.billingEmails).map((email) => email.toLowerCase()),
    whatsappNumbers: ensure(channels?.whatsappNumbers),
    phones: ensure(channels?.phones),
    portals: ensure(channels?.portals),
  }
}

function mergeChannelsWithPrimary(
  payload: CompanyFormValues,
  base?: CompanyCommunicationChannels
): CompanyCommunicationChannels {
  const channels = normalizeChannels(base ?? payload.communicationChannels)
  const supportEmails = new Set(channels.supportEmails)
  const billingEmails = new Set(channels.billingEmails)
  const phones = new Set(channels.phones)

  if (payload.supportEmail) supportEmails.add(payload.supportEmail.toLowerCase())
  if (payload.billingEmail) billingEmails.add(payload.billingEmail.toLowerCase())
  if (payload.phone) phones.add(payload.phone)

  return {
    supportEmails: Array.from(supportEmails),
    billingEmails: Array.from(billingEmails),
    whatsappNumbers: channels.whatsappNumbers,
    phones: Array.from(phones),
    portals: channels.portals,
  }
}

export function sanitizeCompanyInput(input: unknown, tenantId: string): CompanyFormValues {
  const parsed = companyInputSchema.safeParse(input)
  if (!parsed.success) {
    throw parsed.error
  }

  const raw = parsed.data
  const normalizedName = raw.name?.trim() ?? ""
  const normalizedSlug = ensureSlugValue(raw.slug, normalizedName, raw.slug ?? raw.name ?? undefined)
  const cnpjDigits =
    typeof raw.cnpj === "string" ? raw.cnpj.replace(/\D/g, "").slice(0, 14) : null
  const normalizedContacts = (raw.contacts ?? []).map((contact) => ({
    ...contact,
    email: contact.email?.trim().toLowerCase(),
    phone: sanitizePhone(contact.phone),
    whatsapp: sanitizePhone(contact.whatsapp),
    preference: Array.from(new Set(contact.preference ?? [])),
  }))

  const normalizedLocations = (raw.locations ?? []).map((location) => ({
    ...location,
    responsibleContactId: location.responsibleContactId ?? null,
    serviceWindow: location.serviceWindow ?? { mode: "inherit", periods: [] },
  }))

  const normalizedContracts = (raw.contracts ?? []).map((contract) => ({
    ...contract,
    scope: Array.from(new Set(contract.scope ?? [])),
  }))

  const normalizedTags = Array.from(
    new Set((raw.tags ?? []).map((tag) => tag.trim()).filter(Boolean))
  )

  const normalizedCustomFields = (raw.customFields ?? []).map((field) => ({
    ...field,
    label: field.label.trim(),
    key: field.key.trim(),
  }))

  const normalized: CompanyFormValues = companyFormSchema.parse({
    ...raw,
    tenantId,
    name: normalizedName,
    slug: normalizedSlug,
    legalName: raw.legalName?.trim() ?? null,
    tradeName: raw.tradeName?.trim() ?? null,
    cnpj: cnpjDigits && cnpjDigits.length === 14 ? cnpjDigits : null,
    stateRegistration: raw.stateRegistration?.trim() ?? null,
    primaryCnae: raw.primaryCnae?.trim() ?? null,
    description: raw.description?.trim() ?? null,
    domain: sanitizeDomain(raw.domain),
    phone: sanitizePhone(raw.phone),
    address: raw.address?.trim() ?? null,
    communicationChannels: normalizeChannels(raw.communicationChannels),
    supportEmail: raw.supportEmail?.trim().toLowerCase() ?? null,
    billingEmail: raw.billingEmail?.trim().toLowerCase() ?? null,
    clientDomains: Array.from(
      new Set((raw.clientDomains ?? []).map((domain) => domain.trim().toLowerCase()).filter(Boolean))
    ),
    fiscalAddress: raw.fiscalAddress ?? null,
    regulatedEnvironments: Array.from(new Set(raw.regulatedEnvironments ?? [])),
    contacts: normalizedContacts,
    locations: normalizedLocations,
    contracts: normalizedContracts,
    businessHours: raw.businessHours ?? null,
    sla: raw.sla ?? null,
    tags: normalizedTags,
    customFields: normalizedCustomFields,
    notes: raw.notes?.trim() ?? null,
    privacyPolicy: raw.privacyPolicy
      ? {
          accepted: raw.privacyPolicy.accepted ?? false,
          reference: raw.privacyPolicy.reference ?? null,
          metadata: raw.privacyPolicy.metadata,
        }
      : {
          accepted: false,
          reference: null,
        },
  })

  return normalized
}

export function buildCompanyData(payload: CompanyFormValues, tenantId: string): CompanyCreatePayload {
  const stateRegistrationType = payload.stateRegistrationType
    ? STATE_REGISTRATION_TYPE_TO_PRISMA[payload.stateRegistrationType as CompanyStateRegistrationTypeOption]
    : null

  const communicationChannels = mergeChannelsWithPrimary(payload)
  const privacyPolicyMetadata = payload.privacyPolicy?.metadata ?? null
  const contactPreferencesPayload =
    payload.contactPreferences || payload.supportEmail || payload.billingEmail
      ? {
          ...(payload.contactPreferences ?? {}),
          supportEmail: payload.supportEmail ?? null,
          billingEmail: payload.billingEmail ?? null,
        }
      : null

  const data: CompanyCreatePayload = {
    tenantId,
    name: payload.name.trim(),
    slug: payload.slug.trim(),
    provisioningCode: payload.slug.trim(),
    isAvulso: payload.isAvulso ?? false,
    contractedHoursPerMonth: payload.contractedHoursPerMonth ?? null,
    cnpj: payload.cnpj ?? null,
    domain: payload.domain ?? null,
    phone: payload.phone ?? null,
    description: payload.description ?? null,
    address: payload.address ?? null,
    legalName: payload.legalName ?? null,
    tradeName: payload.tradeName ?? null,
    stateRegistration: payload.stateRegistration ?? null,
    stateRegistrationType,
    primaryCnae: payload.primaryCnae ?? null,
    timezone: payload.businessHours?.timezone ?? null,
    businessHours: toJsonInput(payload.businessHours),
    supportEmail: payload.supportEmail ?? null,
    billingEmail: payload.billingEmail ?? null,
    contactPreferences: toJsonInput(contactPreferencesPayload),
    clientDomains: toJsonInput(payload.clientDomains),
    communicationChannels: toJsonInput(communicationChannels),
    fiscalAddress: toJsonInput(payload.fiscalAddress),
    hasBranches: payload.hasBranches ?? false,
    regulatedEnvironments: toJsonInput(payload.regulatedEnvironments),
    privacyPolicyAccepted: payload.privacyPolicy?.accepted ?? false,
    privacyPolicyReference: payload.privacyPolicy?.reference ?? null,
    privacyPolicyMetadata: toJsonInput(privacyPolicyMetadata),
    contacts: toJsonInput(payload.contacts),
    locations: toJsonInput(payload.locations),
    contracts: toJsonInput(payload.contracts),
    sla: toJsonInput(payload.sla),
    tags: toJsonInput(payload.tags),
    customFields: toJsonInput(payload.customFields),
    notes: payload.notes ?? null,
  }

  return data
}

export function normalizeCompany(company: DbCompany): NormalizedCompany {
  const communicationChannels = normalizeChannels(
    company.communicationChannels as CompanyCommunicationChannels | null | undefined
  )
  const normalizedName = (company.name ?? "").trim()
  const normalizedSlug = ensureSlugValue(company.slug, normalizedName || company.name, company.id)
  const base: CompanyFormValues = {
    tenantId: company.tenantId,
    name: normalizedName,
    slug: normalizedSlug,
    legalName: company.legalName,
    tradeName: company.tradeName,
    cnpj: company.cnpj,
    stateRegistration: company.stateRegistration,
    stateRegistrationType: (() => {
      const t = asDbStateRegistrationType(company.stateRegistrationType)
      return t ? STATE_REGISTRATION_TYPE_FROM_PRISMA[t] : undefined
    })(),
    primaryCnae: company.primaryCnae,
    description: company.description,
    domain: company.domain,
    phone: company.phone,
    address: company.address,
    contractedHoursPerMonth: company.contractedHoursPerMonth,
    businessHours: (company.businessHours as CompanyFormValues["businessHours"]) ?? null,
    communicationChannels,
    supportEmail: company.supportEmail,
    billingEmail: company.billingEmail,
    contactPreferences: (company.contactPreferences as CompanyFormValues["contactPreferences"]) ?? undefined,
    clientDomains: (company.clientDomains as string[] | null) ?? [],
    fiscalAddress: (company.fiscalAddress as CompanyFormValues["fiscalAddress"]) ?? null,
    hasBranches: Boolean(company.hasBranches),
    regulatedEnvironments: (company.regulatedEnvironments as string[] | null) ?? [],
    privacyPolicy: {
      accepted: Boolean(company.privacyPolicyAccepted),
      reference: company.privacyPolicyReference ?? null,
      metadata: asJsonRecord(company.privacyPolicyMetadata),
    },
    contacts: (company.contacts as CompanyFormValues["contacts"]) ?? [],
    locations: (company.locations as CompanyFormValues["locations"]) ?? [],
    contracts: (company.contracts as CompanyFormValues["contracts"]) ?? [],
    sla: (company.sla as CompanyFormValues["sla"]) ?? null,
    tags: (company.tags as string[] | null) ?? [],
    customFields: (company.customFields as CompanyFormValues["customFields"]) ?? [],
    notes: company.notes ?? null,
    isAvulso: Boolean(company.isAvulso),
  }

  const payload = companyFormSchema.parse({
    ...base,
    communicationChannels: mergeChannelsWithPrimary(base, communicationChannels),
  })

  return {
    ...payload,
    id: company.id,
    provisioningCode: company.provisioningCode && company.provisioningCode.trim().length > 0
      ? company.provisioningCode
      : null,
    createdAt: company.createdAt.toISOString(),
    updatedAt: company.updatedAt.toISOString(),
  }
}

export async function fetchCompaniesByTenant(tenantId: string): Promise<DbCompany[]> {
  return safeCompanyFindMany({
    where: { tenantId },
    orderBy: { name: "asc" },
  })
}

export async function fetchCompanyById(id: string): Promise<DbCompany | null> {
  const rows = await safeCompanyFindMany({
    where: { id },
    take: 1,
  })
  return rows[0] ?? null
}
