import { z } from 'zod';
import * as attachQ from '@/server/db/queries/support-attachments.js';
import { safeFetchImage } from '@/server/security/safe-fetch-image.js';
import { captureCaught } from '@/server/observability/capture.server.js';
import { env } from '@/server/env.js';
import type { SupportTool, ToolResult } from './_types.js';
import type { AgentDeps } from '../types.js';

const inputSchema = z.object({ attachmentId: z.uuid() });

type Output = { base64: string; mimeType: string };

async function impl(deps: AgentDeps, rawInput: unknown): Promise<ToolResult<Output>> {
  const parsed = inputSchema.safeParse(rawInput);
  if (!parsed.success) return { ok: false, error: `INVALID_INPUT:${parsed.error.message}` };

  const attachment = await attachQ.findById(deps.db, parsed.data.attachmentId);
  if (!attachment) return { ok: false, error: 'NOT_FOUND:attachment' };

  // Verify attachment belongs to the parent this agent is handling
  if (attachment.parentType !== deps.parent.type || attachment.parentId !== deps.parent.id) {
    return { ok: false, error: 'AUTONOMY_GATE:attachment_not_owned' };
  }

  const imageUrl = attachment.variants.full;
  if (!imageUrl) return { ok: false, error: 'NOT_FOUND:image_variant_full' };

  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 10_000);
  let arrayBuf: ArrayBuffer;
  try {
    const fetched = await safeFetchImage(imageUrl, {
      siteUrl: env.PUBLIC_SITE_URL,
      signal: controller.signal,
    });
    arrayBuf = fetched.imageBytes;
  } catch (err) {
    captureCaught(err, { scope: 'server.ai.support.read-image.fetch', severity: 'warning' });
    return { ok: false, error: 'FETCH_ERROR:image_unavailable' };
  } finally {
    clearTimeout(timeoutId);
  }

  if (arrayBuf.byteLength === 0) return { ok: false, error: 'FETCH_EMPTY' };

  // Convert to base64 in chunks to avoid stack overflow
  const bytes = new Uint8Array(arrayBuf);
  const CHUNK = 8192;
  const parts: string[] = [];
  for (let i = 0; i < bytes.length; i += CHUNK) {
    parts.push(String.fromCharCode(...bytes.subarray(i, i + CHUNK)));
  }
  const base64 = btoa(parts.join(''));

  // Mark abuse status as clean (CF Images scans on upload; we just confirm fetch succeeded)
  await attachQ.updateAbuseStatus(deps.db, attachment.id, 'clean');

  // Store in runState for agent loop to use in next generateTextWithImage call
  deps.runState.pendingImageData = { base64, mimeType: attachment.mime };

  return { ok: true, data: { base64, mimeType: attachment.mime } };
}

export const readImageTool = {
  name: 'read_image',
  inputSchema,
  impl,
} satisfies SupportTool<'read_image', z.infer<typeof inputSchema>, Output>;
