import { z } from 'zod';
import { existsSync } from 'node:fs';
import { writeFile, unlink } from 'node:fs/promises';
import { join } from 'node:path';
import type { ToolDef } from '../tools/types.js';
import { ensureMemoryScaffold, getMemoryRoot } from '../memory/paths.js';
import { writeEntry, readTopicFiles } from '../memory/store.js';

const ManageMemoryInput = z.object({
  topic: z.enum(['project', 'user', 'reference']).describe('Memory topic category'),
  title: z.string().min(1).max(200).describe('Entry title (used as identifier for update/delete)'),
  content: z.string().optional().describe('Memory content body (required for create/update)'),
  tags: z.array(z.string()).optional().describe('Optional tags for search'),
  action: z.enum(['create', 'update', 'delete']).describe('Action to perform'),
});

type InputT = z.infer<typeof ManageMemoryInput>;

function titleToFilename(title: string): string {
  return (
    title
      .toLowerCase()
      .replace(/[^a-z0-9]+/g, '-')
      .replace(/(^-|-$)/g, '') + '.md'
  );
}

/**
 * Try to update the CoreTier SQLite database in addition to the file store.
 * This is best-effort — CoreTier is optional (file store is primary).
 */
async function syncCoreTier(
  _root: string,
  _topic: string,
  _title: string,
  _content: string,
  _tags: string[],
): Promise<void> {
  // CoreTier SQLite backend is not available in this build — file store only.
}

export const manageMemoryTool: ToolDef<InputT> = {
  name: 'manage_memory',
  description:
    'Create, update, or delete memory entries in .boocode/memory/ for cross-session recall. ' +
    'Use to persist project conventions, user preferences, and architectural decisions. ' +
    'Actions: create (write new entry), update (modify existing entry), delete (remove entry).',
  inputSchema: ManageMemoryInput,
  jsonSchema: {
    type: 'function',
    function: {
      name: 'manage_memory',
      description: 'Manage memory entries — create, update, or delete',
      parameters: {
        type: 'object',
        properties: {
          topic: {
            type: 'string',
            enum: ['project', 'user', 'reference'],
            description: 'Memory topic category',
          },
          title: { type: 'string', description: 'Entry title (identifier for update/delete)' },
          content: {
            type: 'string',
            description: 'Memory content body (required for create/update)',
          },
          tags: {
            type: 'array',
            items: { type: 'string' },
            description: 'Optional tags for search',
          },
          action: {
            type: 'string',
            enum: ['create', 'update', 'delete'],
            description: 'Action to perform',
          },
        },
        required: ['topic', 'title', 'action'],
      },
    },
  },
  async execute(input: InputT, projectRoot: string): Promise<unknown> {
    const root = getMemoryRoot(projectRoot);
    await ensureMemoryScaffold(root);
    const filename = titleToFilename(input.title);

    if (input.action === 'create') {
      if (!input.content) {
        return { error: 'Content is required for create action.' };
      }
      await writeEntry(root, input.topic, input.title, input.content, input.tags ?? []);
      await syncCoreTier(root, input.topic, input.title, input.content, input.tags ?? []);
      return {
        result: `Memory entry "${input.title}" created in .boocode/memory/${input.topic}/`,
      };
    }

    if (input.action === 'update') {
      if (!input.content) {
        return { error: 'Content is required for update action.' };
      }

      // Resolve target file path — try computed filename first, then heading match
      let targetPath = join(root, input.topic, filename);
      if (!existsSync(targetPath)) {
        const files = await readTopicFiles(root, input.topic);
        const matched = [...files.keys()].find((name) => {
          const content = files.get(name);
          return content?.trimStart().startsWith(`## ${input.topic}: ${input.title}`);
        });
        if (matched) {
          targetPath = join(root, input.topic, matched);
        } else {
          return {
            error: `Memory entry "${input.title}" not found in .boocode/memory/${input.topic}/`,
          };
        }
      }

      const tagLine =
        (input.tags ?? []).length > 0
          ? `> tags: ${(input.tags ?? []).join(', ')}\n\n`
          : '\n';
      const entry = `## ${input.topic}: ${input.title}\n${tagLine}${input.content}\n`;
      await writeFile(targetPath, entry, 'utf8');

      await syncCoreTier(root, input.topic, input.title, input.content, input.tags ?? []);
      return {
        result: `Memory entry "${input.title}" updated in .boocode/memory/${input.topic}/`,
      };
    }

    if (input.action === 'delete') {
      // Resolve target file path
      let targetPath = join(root, input.topic, filename);
      if (!existsSync(targetPath)) {
        const files = await readTopicFiles(root, input.topic);
        const matched = [...files.keys()].find((name) => {
          const content = files.get(name);
          return content?.trimStart().startsWith(`## ${input.topic}: ${input.title}`);
        });
        if (matched) {
          targetPath = join(root, input.topic, matched);
        } else {
          return {
            error: `Memory entry "${input.title}" not found in .boocode/memory/${input.topic}/`,
          };
        }
      }

      await unlink(targetPath);

      return {
        result: `Memory entry "${input.title}" deleted from .boocode/memory/${input.topic}/`,
      };
    }

    return { error: `Unknown action: ${input.action}` };
  },
};
