/**
 * Clause Library Management
 * Handles reusable clause library for document assembly
 */

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

export const ClauseLibrarySchema = z.object({
  id: z.string().uuid(),
  name: z.string(),
  title: z.string().optional(),
  clause_text: z.string(),
  category: z.string().optional(),
  subcategory: z.string().optional(),
  jurisdiction: z.string().optional(),
  practice_area: z.string().optional(),
  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 ClauseLibrary = z.infer<typeof ClauseLibrarySchema>;

export interface CreateClauseInput {
  name: string;
  title?: string;
  clause_text: string;
  category?: string;
  subcategory?: string;
  jurisdiction?: string;
  practice_area?: string;
  variables?: Record<string, unknown>;
  metadata?: Record<string, unknown>;
  version?: number;
  is_active?: boolean;
  is_public?: boolean;
  tags?: string[];
  created_by?: string;
}

/**
 * Create a clause
 */
export async function createClause(input: CreateClauseInput): Promise<ClauseLibrary> {
  const result = await query<ClauseLibrary>(
    `INSERT INTO clause_library 
     (name, title, clause_text, category, subcategory, jurisdiction, practice_area,
      variables, metadata, version, is_active, is_public, tags, created_by)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
     RETURNING *`,
    [
      input.name,
      input.title || null,
      input.clause_text,
      input.category || null,
      input.subcategory || null,
      input.jurisdiction || null,
      input.practice_area || null,
      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 clause by ID
 */
export async function getClause(id: string): Promise<ClauseLibrary | null> {
  const result = await query<ClauseLibrary>(
    `SELECT * FROM clause_library WHERE id = $1`,
    [id]
  );
  return result.rows[0] || null;
}

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

/**
 * List clauses with filters
 */
export interface ClauseFilters {
  category?: string;
  subcategory?: string;
  jurisdiction?: string;
  practice_area?: string;
  is_active?: boolean;
  is_public?: boolean;
  tags?: string[];
  search?: string;
}

export async function listClauses(
  filters: ClauseFilters = {},
  limit = 100,
  offset = 0
): Promise<ClauseLibrary[]> {
  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.jurisdiction) {
    conditions.push(`jurisdiction = $${paramIndex++}`);
    params.push(filters.jurisdiction);
  }

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

  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 title ILIKE $${paramIndex} OR clause_text ILIKE $${paramIndex})`
    );
    params.push(`%${filters.search}%`);
    paramIndex++;
  }

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

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

  return result.rows;
}

/**
 * Update clause
 */
export async function updateClause(
  id: string,
  updates: Partial<
    Pick<
      ClauseLibrary,
      | 'title'
      | 'clause_text'
      | 'category'
      | 'subcategory'
      | 'jurisdiction'
      | 'practice_area'
      | 'variables'
      | 'metadata'
      | 'is_active'
      | 'tags'
    >
  >
): Promise<ClauseLibrary | null> {
  const fields: string[] = [];
  const values: unknown[] = [];
  let paramIndex = 1;

  const fieldMap: Record<string, string> = {
    title: 'title',
    clause_text: 'clause_text',
    category: 'category',
    subcategory: 'subcategory',
    jurisdiction: 'jurisdiction',
    practice_area: 'practice_area',
    variables: 'variables',
    metadata: 'metadata',
    is_active: 'is_active',
    tags: 'tags',
  };

  for (const [key, dbField] of Object.entries(fieldMap)) {
    if (key in updates && updates[key as keyof typeof updates] !== undefined) {
      const value = updates[key as keyof typeof updates];
      if (key === 'variables' || key === 'metadata') {
        fields.push(`${dbField} = $${paramIndex++}`);
        values.push(JSON.stringify(value));
      } else {
        fields.push(`${dbField} = $${paramIndex++}`);
        values.push(value);
      }
    }
  }

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

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

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

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

/**
 * Create new clause version
 */
export async function createClauseVersion(
  clauseId: string,
  updates: Partial<Pick<ClauseLibrary, 'clause_text' | 'title' | 'variables' | 'metadata'>>
): Promise<ClauseLibrary> {
  const original = await getClause(clauseId);
  if (!original) {
    throw new Error(`Clause ${clauseId} not found`);
  }

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

  return createClause({
    name: original.name,
    title: updates.title || original.title,
    clause_text: updates.clause_text || original.clause_text,
    category: original.category,
    subcategory: original.subcategory,
    jurisdiction: original.jurisdiction,
    practice_area: original.practice_area,
    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 clause with variables
 */
export function renderClause(
  clause: ClauseLibrary,
  variables: Record<string, unknown>
): string {
  let rendered = clause.clause_text;

  // Replace {{variable}} patterns
  rendered = rendered.replace(/\{\{(\w+)\}\}/g, (match, varName) => {
    const value = variables[varName];
    return value !== undefined && value !== null ? String(value) : match;
  });

  // 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;
      }
    }
    return value !== undefined && value !== null ? String(value) : match;
  });

  return rendered;
}

/**
 * Get clause usage statistics
 */
export interface ClauseUsageStats {
  clause_id: string;
  clause_name: string;
  usage_count: number;
  last_used?: Date;
}

export async function getClauseUsageStats(
  clause_id: string
): Promise<ClauseUsageStats | null> {
  // This would query a usage tracking table (to be created)
  // For now, return basic info
  const clause = await getClause(clause_id);
  if (!clause) {
    return null;
  }

  return {
    clause_id: clause.id,
    clause_name: clause.name,
    usage_count: 0, // TODO: Implement usage tracking
    last_used: undefined,
  };
}

