import type {
  MediaAsset,
  MediaKind,
  MediaSource,
  MediaUsage,
  MediaUsageType,
} from "@prisma/client";

import { prisma } from "@/lib/prisma";

export type MediaAssetView = Pick<
  MediaAsset,
  "id" | "source" | "kind" | "url" | "fileName" | "label" | "altText" | "mimeType" | "size" | "createdAt"
> & {
  usages: Array<
    Pick<MediaUsage, "id" | "usageType" | "entityType" | "entityId" | "fieldKey">
  >;
};

export type MediaOption = Pick<MediaAssetView, "id" | "kind" | "url" | "label" | "source">;

export type PortfolioMediaBindings = {
  coverAssetId: string | null;
  sectionAssetIds: Record<string, string>;
  assetIds: Record<string, string>;
};

function mapMediaAsset(
  asset: MediaAsset & {
    usages: MediaUsage[];
  },
): MediaAssetView {
  return {
    id: asset.id,
    source: asset.source,
    kind: asset.kind,
    url: asset.url,
    fileName: asset.fileName,
    label: asset.label,
    altText: asset.altText,
    mimeType: asset.mimeType,
    size: asset.size,
    createdAt: asset.createdAt,
    usages: asset.usages.map((usage) => ({
      id: usage.id,
      usageType: usage.usageType,
      entityType: usage.entityType,
      entityId: usage.entityId,
      fieldKey: usage.fieldKey,
    })),
  };
}

export async function getAdminMediaAssets() {
  const assets = await prisma.mediaAsset.findMany({
    include: {
      usages: {
        orderBy: [{ createdAt: "desc" }],
      },
    },
    orderBy: [{ createdAt: "desc" }],
  });

  return assets.map(mapMediaAsset);
}

export async function getMediaOptions(filters?: { kind?: MediaKind }) {
  const assets = await prisma.mediaAsset.findMany({
    where: filters?.kind ? { kind: filters.kind } : undefined,
    orderBy: [{ createdAt: "desc" }],
    select: {
      id: true,
      kind: true,
      url: true,
      label: true,
      source: true,
    },
  });

  return assets;
}

export async function getMediaAssetById(id: string) {
  const asset = await prisma.mediaAsset.findUnique({
    where: { id },
    include: {
      usages: true,
    },
  });

  return asset ? mapMediaAsset(asset) : null;
}

export async function createMediaAsset(input: {
  source: MediaSource;
  kind: MediaKind;
  url: string;
  fileName: string;
  label: string;
  altText?: string | null;
  mimeType?: string | null;
  size?: number | null;
}) {
  return prisma.mediaAsset.create({
    data: {
      source: input.source,
      kind: input.kind,
      url: input.url,
      fileName: input.fileName,
      label: input.label,
      altText: input.altText ?? null,
      mimeType: input.mimeType ?? null,
      size: input.size ?? null,
    },
  });
}

export async function replaceEntityMediaUsages(input: {
  entityType: string;
  entityId: string;
  usages: Array<{
    assetId: string;
    usageType: MediaUsageType;
    fieldKey: string;
  }>;
}) {
  await prisma.$transaction(async (tx) => {
    await tx.mediaUsage.deleteMany({
      where: {
        entityType: input.entityType,
        entityId: input.entityId,
      },
    });

    if (input.usages.length === 0) {
      return;
    }

    await tx.mediaUsage.createMany({
      data: input.usages.map((usage) => ({
        assetId: usage.assetId,
        usageType: usage.usageType,
        entityType: input.entityType,
        entityId: input.entityId,
        fieldKey: usage.fieldKey,
      })),
    });
  });
}

export async function deleteEntityMediaUsages(entityType: string, entityId: string) {
  await prisma.mediaUsage.deleteMany({
    where: {
      entityType,
      entityId,
    },
  });
}

export async function getPortfolioMediaBindings(projectId: string): Promise<PortfolioMediaBindings> {
  const usages = await prisma.mediaUsage.findMany({
    where: {
      entityType: "portfolio-project",
      entityId: projectId,
    },
    select: {
      assetId: true,
      usageType: true,
      fieldKey: true,
    },
  });

  return usages.reduce<PortfolioMediaBindings>(
    (result, usage) => {
      if (usage.usageType === "PORTFOLIO_COVER") {
        result.coverAssetId = usage.assetId;
      }

      if (usage.usageType === "PORTFOLIO_SECTION") {
        result.sectionAssetIds[usage.fieldKey] = usage.assetId;
      }

      if (usage.usageType === "PORTFOLIO_ASSET") {
        result.assetIds[usage.fieldKey] = usage.assetId;
      }

      return result;
    },
    {
      coverAssetId: null,
      sectionAssetIds: {},
      assetIds: {},
    },
  );
}

export async function countMediaUsageReferences(assetId: string) {
  return prisma.mediaUsage.count({
    where: {
      assetId,
    },
  });
}
