import { eq, and, desc, sql } from 'drizzle-orm';
import { db } from '../db/index';
import { assets } from '../db/schema';
import { eventBus } from '../events/bus';
import { createHash } from 'crypto';
import { v4 as uuidv4 } from 'uuid';

// ═══════════════════════════════════════════════════════════
// Asset Service — File storage operations
// ═══════════════════════════════════════════════════════════

export async function listAssets(
  pipelineId: string,
  opts: { type?: string; stage?: string; page?: number; limit?: number }
) {
  const { page = 1, limit = 20 } = opts;
  const offset = (page - 1) * limit;

  const conditions = [eq(assets.pipelineId, pipelineId)];
  if (opts.type) conditions.push(eq(assets.type, opts.type));
  if (opts.stage) conditions.push(eq(assets.stageName, opts.stage));

  const where = and(...conditions);

  const [items, countResult] = await Promise.all([
    db
      .select()
      .from(assets)
      .where(where)
      .orderBy(desc(assets.createdAt))
      .limit(limit)
      .offset(offset),
    db
      .select({ count: sql<number>`count(*)` })
      .from(assets)
      .where(where),
  ]);

  const total = Number(countResult[0]?.count || 0);

  return {
    items,
    meta: { page, limit, total, totalPages: Math.ceil(total / limit) },
  };
}

export async function getAsset(id: string) {
  return db.query.assets.findFirst({
    where: eq(assets.id, id),
    with: { pipeline: true },
  });
}

export async function createAsset(data: {
  pipelineId: string;
  stage?: string;
  type: string;
  name: string;
  path?: string;
  contentType?: string;
  fileBuffer?: Buffer;
  metadata?: Record<string, unknown>;
  generatedBy?: string;
  generatorType?: string;
}) {
  const now = new Date();
  const storageKey = `assets/${data.pipelineId}/${now.getTime()}/${data.name}`;

  // Compute checksum if we have a buffer
  let checksumSha256: string | undefined;
  let sizeBytes: number | undefined;
  if (data.fileBuffer) {
    checksumSha256 = createHash('sha256').update(data.fileBuffer).digest('hex');
    sizeBytes = data.fileBuffer.length;
  }

  // Check for existing versions of this asset (same name + pipeline)
  const existing = await db
    .select()
    .from(assets)
    .where(
      and(
        eq(assets.pipelineId, data.pipelineId),
        eq(assets.name, data.name)
      )
    )
    .orderBy(desc(assets.version))
    .limit(1);

  const previousVersion = existing[0];
  const version = previousVersion ? (previousVersion.version || 1) + 1 : 1;

  // In production: upload to S3/R2 here
  // For now: store metadata only

  const [asset] = await db
    .insert(assets)
    .values({
      pipelineId: data.pipelineId,
      stageName: data.stage || null,
      type: data.type,
      name: data.name,
      path: data.path || null,
      storageKey,
      storageBucket: 'goosefactory-assets',
      sizeBytes: sizeBytes || 0,
      contentType: data.contentType || 'application/octet-stream',
      checksumSha256: checksumSha256 || null,
      version,
      previousVersionId: previousVersion?.id || null,
      generatedBy: data.generatedBy || null,
      generatorType: data.generatorType || 'user',
      metadata: data.metadata || {},
    })
    .returning();

  // Emit event
  await eventBus.emit({
    type: 'asset.created',
    timestamp: now.toISOString(),
    data: {
      assetId: asset.id,
      pipelineId: data.pipelineId,
      type: data.type,
      name: data.name,
      version,
      sizeBytes: sizeBytes || 0,
    },
    actor: data.generatedBy
      ? { type: data.generatorType === 'agent' ? 'agent' : 'user', id: data.generatedBy }
      : { type: 'system', id: 'system' },
  });

  return asset;
}

export async function getAssetDownloadInfo(id: string) {
  const asset = await db.query.assets.findFirst({
    where: eq(assets.id, id),
  });

  if (!asset) return null;

  // In production: generate a presigned URL from S3/R2
  return {
    asset,
    downloadUrl: `/v1/assets/${id}/download`,
    storageKey: asset.storageKey,
    contentType: asset.contentType || 'application/octet-stream',
    sizeBytes: asset.sizeBytes,
  };
}

export async function getAssetDiff(
  assetId: string,
  fromVersion: number,
  toVersion?: number
) {
  const asset = await db.query.assets.findFirst({
    where: eq(assets.id, assetId),
  });

  if (!asset) return null;

  // Get the from-version asset
  const fromAsset = await db
    .select()
    .from(assets)
    .where(
      and(
        eq(assets.pipelineId, asset.pipelineId!),
        eq(assets.name, asset.name),
        eq(assets.version, fromVersion)
      )
    )
    .limit(1);

  if (!fromAsset[0]) return null;

  // Get the to-version asset (default: latest = current asset if toVersion matches)
  let toAsset = asset;
  if (toVersion && toVersion !== asset.version) {
    const found = await db
      .select()
      .from(assets)
      .where(
        and(
          eq(assets.pipelineId, asset.pipelineId!),
          eq(assets.name, asset.name),
          eq(assets.version, toVersion)
        )
      )
      .limit(1);
    if (!found[0]) return null;
    toAsset = found[0];
  }

  // In production: fetch file contents from S3 and compute actual diff
  // For now: return metadata-level diff
  return {
    assetName: asset.name,
    fromVersion,
    toVersion: toAsset.version || toVersion || asset.version,
    filesChanged: 1,
    insertions: 0,
    deletions: 0,
    fromSize: fromAsset[0].sizeBytes || 0,
    toSize: toAsset.sizeBytes || 0,
    fromChecksum: fromAsset[0].checksumSha256,
    toChecksum: toAsset.checksumSha256,
    files: [
      {
        path: asset.name,
        status: 'modified' as const,
        insertions: 0,
        deletions: 0,
      },
    ],
  };
}
