/**
 * Document Template Management
 * Handles legal document templates with variable substitution
 */

import { query } from './client';
import { z } from 'zod';

export const DocumentTemplateSchema = z.object({
  id: z.string().uuid(),
  name: z.string(),
  description: z.string().optional(),
  category: z.string().optional(),
  subcategory: z.string().optional(),
  template_content: z.string(),
  variables: z.record(z.unknown()).optional(),
  metadata: z.record(z.unknown()).optional(),
  version: z.number().int().positive(),
  is_active: z.boolean(),
  is_public: z.boolean(),
  tags: z.array(z.string()).optional(),
  created_by: z.string().uuid().optional(),
  created_at: z.date(),
  updated_at: z.date(),
});

export type DocumentTemplate = z.infer<typeof DocumentTemplateSchema>;

export interface CreateDocumentTemplateInput {
  name: string;
  description?: string;
  category?: string;
  subcategory?: string;
  template_content: string;
  variables?: Record<string, unknown>;
  metadata?: Record<string, unknown>;
  version?: number;
  is_active?: boolean;
  is_public?: boolean;
  tags?: string[];
  created_by?: string;
}

/**
 * Create a document template
 */
export async function createDocumentTemplate(
  input: CreateDocumentTemplateInput
): Promise<DocumentTemplate> {
  const result = await query<DocumentTemplate>(
    `INSERT INTO document_templates 
     (name, description, category, subcategory, template_content, variables, 
      metadata, version, is_active, is_public, tags, created_by)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
     RETURNING *`,
    [
      input.name,
      input.description || null,
      input.category || null,
      input.subcategory || null,
      input.template_content,
      input.variables ? JSON.stringify(input.variables) : null,
      input.metadata ? JSON.stringify(input.metadata) : null,
      input.version || 1,
      input.is_active !== false,
      input.is_public || false,
      input.tags || [],
      input.created_by || null,
    ]
  );

  return result.rows[0]!;
}

/**
 * Get template by ID
 */
export async function getDocumentTemplate(id: string): Promise<DocumentTemplate | null> {
  const result = await query<DocumentTemplate>(
    `SELECT * FROM document_templates WHERE id = $1`,
    [id]
  );
  return result.rows[0] || null;
}

/**
 * Get template by name and version
 */
export async function getDocumentTemplateByName(
  name: string,
  version?: number
): Promise<DocumentTemplate | null> {
  if (version) {
    const result = await query<DocumentTemplate>(
      `SELECT * FROM document_templates 
       WHERE name = $1 AND version = $2`,
      [name, version]
    );
    return result.rows[0] || null;
  } else {
    // Get latest active version
    const result = await query<DocumentTemplate>(
      `SELECT * FROM document_templates 
       WHERE name = $1 AND is_active = TRUE 
       ORDER BY version DESC 
       LIMIT 1`,
      [name]
    );
    return result.rows[0] || null;
  }
}

/**
 * List templates with filters
 */
export interface TemplateFilters {
  category?: string;
  subcategory?: string;
  is_active?: boolean;
  is_public?: boolean;
  tags?: string[];
  search?: string;
}

export async function listDocumentTemplates(
  filters: TemplateFilters = {},
  limit = 100,
  offset = 0
): Promise<DocumentTemplate[]> {
  const conditions: string[] = [];
  const params: unknown[] = [];
  let paramIndex = 1;

  if (filters.category) {
    conditions.push(`category = $${paramIndex++}`);
    params.push(filters.category);
  }

  if (filters.subcategory) {
    conditions.push(`subcategory = $${paramIndex++}`);
    params.push(filters.subcategory);
  }

  if (filters.is_active !== undefined) {
    conditions.push(`is_active = $${paramIndex++}`);
    params.push(filters.is_active);
  }

  if (filters.is_public !== undefined) {
    conditions.push(`is_public = $${paramIndex++}`);
    params.push(filters.is_public);
  }

  if (filters.tags && filters.tags.length > 0) {
    conditions.push(`tags && $${paramIndex++}`);
    params.push(filters.tags);
  }

  if (filters.search) {
    conditions.push(
      `(name ILIKE $${paramIndex} OR description ILIKE $${paramIndex} OR template_content ILIKE $${paramIndex})`
    );
    params.push(`%${filters.search}%`);
    paramIndex++;
  }

  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';

  const result = await query<DocumentTemplate>(
    `SELECT * FROM document_templates 
     ${whereClause}
     ORDER BY name, version DESC 
     LIMIT $${paramIndex++} OFFSET $${paramIndex++}`,
    [...params, limit, offset]
  );

  return result.rows;
}

/**
 * Update template
 */
export async function updateDocumentTemplate(
  id: string,
  updates: Partial<
    Pick<
      DocumentTemplate,
      'description' | 'template_content' | 'variables' | 'metadata' | 'is_active' | 'tags'
    >
  >
): Promise<DocumentTemplate | null> {
  const fields: string[] = [];
  const values: unknown[] = [];
  let paramIndex = 1;

  if (updates.description !== undefined) {
    fields.push(`description = $${paramIndex++}`);
    values.push(updates.description);
  }

  if (updates.template_content !== undefined) {
    fields.push(`template_content = $${paramIndex++}`);
    values.push(updates.template_content);
  }

  if (updates.variables !== undefined) {
    fields.push(`variables = $${paramIndex++}`);
    values.push(JSON.stringify(updates.variables));
  }

  if (updates.metadata !== undefined) {
    fields.push(`metadata = $${paramIndex++}`);
    values.push(JSON.stringify(updates.metadata));
  }

  if (updates.is_active !== undefined) {
    fields.push(`is_active = $${paramIndex++}`);
    values.push(updates.is_active);
  }

  if (updates.tags !== undefined) {
    fields.push(`tags = $${paramIndex++}`);
    values.push(updates.tags);
  }

  if (fields.length === 0) {
    return getDocumentTemplate(id);
  }

  fields.push(`updated_at = NOW()`);
  values.push(id);

  const result = await query<DocumentTemplate>(
    `UPDATE document_templates 
     SET ${fields.join(', ')}
     WHERE id = $${paramIndex}
     RETURNING *`,
    values
  );

  return result.rows[0] || null;
}

/**
 * Create new template version
 */
export async function createTemplateVersion(
  templateId: string,
  updates: Partial<Pick<DocumentTemplate, 'template_content' | 'description' | 'variables' | 'metadata'>>
): Promise<DocumentTemplate> {
  const original = await getDocumentTemplate(templateId);
  if (!original) {
    throw new Error(`Template ${templateId} not found`);
  }

  // Get next version number
  const versionResult = await query<{ max_version: number }>(
    `SELECT MAX(version) as max_version FROM document_templates WHERE name = $1`,
    [original.name]
  );
  const nextVersion = (versionResult.rows[0]?.max_version || 0) + 1;

  return createDocumentTemplate({
    name: original.name,
    description: updates.description || original.description,
    category: original.category,
    subcategory: original.subcategory,
    template_content: updates.template_content || original.template_content,
    variables: updates.variables || original.variables,
    metadata: updates.metadata || original.metadata,
    version: nextVersion,
    is_active: true,
    is_public: original.is_public,
    tags: original.tags,
    created_by: original.created_by,
  });
}

/**
 * Render template with variables
 * Supports {{variable_name}} syntax
 */
export function renderDocumentTemplate(
  template: DocumentTemplate,
  variables: Record<string, unknown>
): string {
  let rendered = template.template_content;

  // Replace {{variable}} patterns
  rendered = rendered.replace(/\{\{(\w+)\}\}/g, (match, varName) => {
    const value = variables[varName];
    if (value === undefined || value === null) {
      return match; // Keep placeholder if variable not provided
    }
    return String(value);
  });

  // Support nested variables {{object.property}}
  rendered = rendered.replace(/\{\{(\w+(?:\.\w+)+)\}\}/g, (match, path) => {
    const parts = path.split('.');
    let value: unknown = variables;
    for (const part of parts) {
      if (value && typeof value === 'object' && part in value) {
        value = (value as Record<string, unknown>)[part];
      } else {
        return match; // Keep placeholder if path not found
      }
    }
    return value !== undefined && value !== null ? String(value) : match;
  });

  return rendered;
}

/**
 * Extract variables from template
 */
export function extractTemplateVariables(template_content: string): string[] {
  const variables = new Set<string>();
  const matches = template_content.matchAll(/\{\{(\w+(?:\.\w+)*)\}\}/g);
  
  for (const match of matches) {
    const variableName = match[1];
    if (variableName) {
      variables.add(variableName);
    }
  }

  return Array.from(variables).sort();
}
