import type {
  PolicyBatchSetEnabledRequest,
  PolicyBatchSetEnabledResponse,
  PolicyDocument,
  PolicyListResponse,
  PolicySetEnabledRequest,
  PolicySummary,
  PolicyValidateResponse,
  PolicyValidationIssue,
  Severity,
} from '@trustloopguard/sdk';
import { z } from 'zod';
import { http } from './http';
import { policyDraftSchema, type PolicyDraft } from './policy-draft';

const severitySchema = z.enum(['low', 'medium', 'high', 'critical']) satisfies z.ZodType<Severity>;

const policySummaryWireSchema = z.object({
  id: z.string(),
  description: z.string().nullable().optional(),
  severity: severitySchema,
  action: z.string().optional(),
  enabled: z.boolean(),
  owner_agent_id: z.string().nullable().optional(),
});

const policyDocumentWireSchema = policySummaryWireSchema.extend({
  source_yaml: z.string(),
});

const policyValidationIssueSchema = z.object({
  path: z.string(),
  message: z.string(),
}) satisfies z.ZodType<PolicyValidationIssue>;

export const policySummarySchema: z.ZodType<PolicySummary> =
  policySummaryWireSchema.transform(toPolicySummary);

const policyDocumentSchema: z.ZodType<PolicyDocument> =
  policyDocumentWireSchema.transform(toPolicyDocument);

const policyValidateResponseSchema: z.ZodType<PolicyValidateResponse> = z
  .object({
    valid: z.boolean(),
    policy_id: z.string().nullable().optional(),
    errors: z.array(policyValidationIssueSchema),
  })
  .transform(
    (result): PolicyValidateResponse => ({
      valid: result.valid,
      errors: result.errors,
      ...(result.policy_id !== undefined && result.policy_id !== null
        ? { policy_id: result.policy_id }
        : {}),
    }),
  );

const policyListResponseSchema: z.ZodType<PolicyListResponse> = z.object({
  policies: z.array(policySummarySchema),
});

const policyBatchSetEnabledResponseSchema: z.ZodType<PolicyBatchSetEnabledResponse> = z.object({
  policies: z.array(policySummarySchema),
});

const generatePolicyDraftResponseSchema = z.object({
  draft: policyDraftSchema,
});

export async function listPolicies(signal?: AbortSignal): Promise<PolicyListResponse> {
  return http.get(withWorkspace('/api/policies'), policyListResponseSchema, { signal });
}

export async function listPoliciesForAgent(
  agentId: string,
  signal?: AbortSignal,
): Promise<PolicyListResponse> {
  return http.get(
    withWorkspace(`/api/policies?agentid=${encodeURIComponent(agentId)}`),
    policyListResponseSchema,
    { signal },
  );
}

export async function getPolicy(policyId: string, signal?: AbortSignal): Promise<PolicyDocument> {
  return http.get(
    withWorkspace(`/api/policies/${encodeURIComponent(policyId)}`),
    policyDocumentSchema,
    { signal },
  );
}

export async function validatePolicy(
  sourceYaml: string,
  signal?: AbortSignal,
): Promise<PolicyValidateResponse> {
  return http.post('/api/policies/validate', sourceYaml, policyValidateResponseSchema, {
    contentType: 'application/yaml',
    signal,
  });
}

export async function upsertPolicy(
  sourceYaml: string,
  signal?: AbortSignal,
): Promise<PolicyDocument> {
  return http.post(withWorkspace('/api/policies'), sourceYaml, policyDocumentSchema, {
    contentType: 'application/yaml',
    signal,
  });
}

export async function setPolicyEnabled(
  policyId: string,
  enabled: boolean,
  signal?: AbortSignal,
): Promise<PolicyDocument> {
  const body = { enabled } satisfies PolicySetEnabledRequest;
  return http.patch(
    withWorkspace(`/api/policies/${encodeURIComponent(policyId)}/enabled`),
    body,
    policyDocumentSchema,
    { signal },
  );
}

export async function setPoliciesEnabled(
  policyIds: string[],
  enabled: boolean,
  signal?: AbortSignal,
): Promise<PolicyBatchSetEnabledResponse> {
  const body = { ids: policyIds, enabled } satisfies PolicyBatchSetEnabledRequest;
  return http.patch(
    withWorkspace('/api/policies/batch/enabled'),
    body,
    policyBatchSetEnabledResponseSchema,
    { signal },
  );
}

export async function deletePolicy(policyId: string, signal?: AbortSignal): Promise<void> {
  await http.delete(withWorkspace(`/api/policies/${encodeURIComponent(policyId)}`), { signal });
}

export async function generatePolicyDraft(
  prompt: string,
  signal?: AbortSignal,
): Promise<PolicyDraft> {
  const result = await http.post(
    '/api/policies/generate',
    { prompt },
    generatePolicyDraftResponseSchema,
    { signal },
  );
  return result.draft;
}

export interface PolicyVersionSummary {
  version: number;
  created_at: string;
}

export interface PolicyVersionListResponse {
  versions: PolicyVersionSummary[];
}

export interface PolicyVersionDetail {
  version: number;
  content: string;
  created_at: string;
}

export async function listPolicyVersions(
  policyId: string,
  signal?: AbortSignal,
): Promise<PolicyVersionListResponse> {
  const res = await fetch(
    withWorkspace(`/api/policies/${encodeURIComponent(policyId)}/versions`),
    { signal: signal ?? null },
  );
  if (!res.ok) throw new Error(`list versions ${res.status}`);
  return res.json() as Promise<PolicyVersionListResponse>;
}

export async function getPolicyVersion(
  policyId: string,
  version: number,
  signal?: AbortSignal,
): Promise<PolicyVersionDetail> {
  const res = await fetch(
    withWorkspace(`/api/policies/${encodeURIComponent(policyId)}/versions/${version}`),
    { signal: signal ?? null },
  );
  if (!res.ok) throw new Error(`get version ${res.status}`);
  return res.json() as Promise<PolicyVersionDetail>;
}

export async function aiEditPolicy(
  yaml: string,
  instruction: string,
  signal?: AbortSignal,
): Promise<string> {
  const res = await fetch(withWorkspace('/api/policies/ai-edit'), {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ yaml, instruction }),
    signal: signal ?? null,
  });
  if (!res.ok) throw new Error(`ai-edit ${res.status}`);
  const data = await res.json() as { yaml: string };
  return data.yaml;
}

export type PolicyValidationResult = z.infer<typeof policyValidateResponseSchema>;

type ParsedPolicySummary = z.infer<typeof policySummaryWireSchema>;
type ParsedPolicyDocument = z.infer<typeof policyDocumentWireSchema>;

function withWorkspace(path: string): string {
  if (typeof window === 'undefined') return path;
  const workspace = new URLSearchParams(window.location.search).get('workspace');
  if (workspace === null || workspace.trim() === '') return path;
  const separator = path.includes('?') ? '&' : '?';
  return `${path}${separator}workspace=${encodeURIComponent(workspace)}`;
}

function toPolicySummary(policy: ParsedPolicySummary): PolicySummary {
  return {
    id: policy.id,
    ...(typeof policy.description === 'string' ? { description: policy.description } : {}),
    severity: policy.severity,
    action: policy.action ?? 'block',
    enabled: policy.enabled,
    ...(policy.owner_agent_id !== undefined && policy.owner_agent_id !== null
      ? { owner_agent_id: policy.owner_agent_id }
      : {}),
  };
}

function toPolicyDocument(policy: ParsedPolicyDocument): PolicyDocument {
  return {
    ...toPolicySummary(policy),
    source_yaml: policy.source_yaml,
  };
}
