/**
 * ComponentFetch Tool — search, fetch, and adapt UI components for VL projects
 *
 * Sources:
 *   1. DocCenter component library (tagged components stored as versioned docs)
 *   2. Web component galleries (shadcn/ui, Ant Design, MUI, etc.)
 *
 * Actions:
 *   search     — search DocCenter for reusable VL component specs
 *   fetch      — download a component from DocCenter, adapt to project context
 *   webSearch  — search web for UI component patterns, return candidates
 *   webFetch   — fetch a web component page and extract its spec for VL adaptation
 *   publish    — save a .cp file back to DocCenter as a reusable component template
 *   list       — list all components in the DocCenter component library
 */

import { DOCCENTER_API_URL } from '../data/doc-paths.js';
const DC_BASE = DOCCENTER_API_URL;
// DocCenter tag name for component library entries
const COMPONENT_TAG_NAME = 'component-library';

export function createComponentFetchTool(config) {
  // Shared HTTP helper for DocCenter
  const dcPost = async (endpoint, body) => {
    const cookie = config.cookie;
    const headers = { 'Content-Type': 'application/json' };
    if (cookie) headers['Cookie'] = `ih5bearer=${cookie}`;
    const res = await fetch(`${DC_BASE}/${endpoint}`, {
      method: 'POST', headers, body: JSON.stringify(body),
    });
    const text = await res.text();
    try { return JSON.parse(text); } catch { return { raw: text }; }
  };

  // Factory doc (path 6) stores a JSON component library
  const FACTORY_DOC_ID = 15;

  // Load Factory component library (cached per execution)
  let _factoryCache = null;
  const loadFactory = async () => {
    if (_factoryCache) return _factoryCache;
    try {
      const res = await dcPost('SERVICE_DocCenter_GetDocById', { docId: FACTORY_DOC_ID });
      const content = res?.data?.currentContent || res?.currentContent || '';
      if (content) {
        const parsed = JSON.parse(content);
        _factoryCache = parsed?.components || [];
      }
    } catch { _factoryCache = []; }
    return _factoryCache || [];
  };

  // Ensure "component-library" tag exists in DocCenter, return its tagId
  const ensureComponentTag = async () => {
    const tagsRes = await dcPost('SERVICE_DocCenter_GetAllTags', {});
    const tags = tagsRes?.data || tagsRes?.result?.data || [];
    const existing = tags.find(t => t.name === COMPONENT_TAG_NAME);
    if (existing) return existing._id || existing.id;
    // Create it
    const createRes = await dcPost('SERVICE_DocCenter_CreateTag', { name: COMPONENT_TAG_NAME, color: 'green' });
    return createRes?.data?._id || createRes?.data?.id || null;
  };

  return {
    description: `Search, fetch, and adapt UI components for VL projects.
Use this to find reusable components from the DocCenter component library or from web component galleries (shadcn, MUI, Ant Design, etc.).
Actions:
  search    — search DocCenter component library by keyword
  fetch     — download a component from DocCenter by docId, returns VL .cp source
  webSearch — search web for UI component patterns (returns candidate list)
  webFetch  — fetch a specific web URL, extract component spec for VL adaptation
  publish   — save a .cp component to DocCenter as reusable template
  list      — list all components in DocCenter component library`,
    parameters: {
      type: 'object',
      properties: {
        action: {
          type: 'string',
          enum: ['search', 'fetch', 'webSearch', 'webFetch', 'publish', 'list'],
          description: 'Action to perform',
        },
        // search / webSearch params
        keyword: { type: 'string', description: 'Search keyword (e.g. "data table", "date picker", "avatar")' },
        category: { type: 'string', description: 'Component category: input|display|feedback|navigation|layout|data|chart' },
        // fetch params
        docId: { type: 'number', description: 'DocCenter document ID to fetch (from search results)' },
        componentName: { type: 'string', description: 'Target component name in PascalCase (e.g. "DatePicker")' },
        // webFetch params
        url: { type: 'string', description: 'URL to fetch component spec from (webFetch action)' },
        // publish params
        name: { type: 'string', description: 'Component name for DocCenter (publish action)' },
        description: { type: 'string', description: 'Component description (publish action)' },
        content: { type: 'string', description: 'VL .cp source code to publish' },
        tags: { type: 'array', items: { type: 'string' }, description: 'Category tags: ["input","form"] etc.' },
      },
      required: ['action'],
    },
    execute: async (input, runtime = {}) => {
      try {
        runtime.info?.('ComponentFetch action started', {
          action: input.action,
          keyword: input.keyword || null,
          componentName: input.componentName || null,
          docId: input.docId || null,
          url: input.url || null,
        });
        switch (input.action) {

          // ─── Search DocCenter component library ───
          case 'search': {
            const keyword = (input.keyword || '').toLowerCase();
            const results = [];

            // 1. Search Factory component library (primary source)
            const factory = await loadFactory();
            for (const comp of factory) {
              if (!keyword ||
                  comp.name?.toLowerCase().includes(keyword) ||
                  comp.description?.toLowerCase().includes(keyword)) {
                results.push({
                  name: comp.name,
                  description: comp.description,
                  props: comp.props,
                  events: comp.events,
                  source: 'factory',
                  hasVLSource: !!comp.vlSource,
                });
              }
            }

            // 2. Also search individual CP: docs
            const res = await dcPost('SERVICE_DocCenter_GetDocList', {
              keyword: input.keyword || '', tagId: 0, page: 1, pageSize: 50,
            });
            const docs = (res?.data || []).filter(d => d.name?.startsWith('CP:'));
            for (const d of docs) {
              if (!results.find(r => r.name === d.name?.replace(/^CP:\s*/, ''))) {
                results.push({
                  docId: d._id,
                  name: d.name?.replace(/^CP:\s*/, ''),
                  description: d.description,
                  source: 'doc',
                });
              }
            }

            runtime.info?.('ComponentFetch search completed', {
              keyword: input.keyword || '',
              count: results.length,
            });
            return {
              result: {
                count: results.length,
                components: results,
                hint: results.length === 0
                  ? `No components found for "${keyword}". Try webSearch to find from online galleries.`
                  : `Found ${results.length} component(s). Use fetch with name to download.`,
              }
            };
          }

          // ─── Fetch component from DocCenter ───
          case 'fetch': {
            const targetName = input.componentName || '';

            // 1. Try Factory library first (by name)
            if (targetName || input.keyword) {
              const factory = await loadFactory();
              const searchName = (targetName || input.keyword || '').toLowerCase();
              const match = factory.find(c => c.name?.toLowerCase() === searchName);
              if (match && match.vlSource) {
                let source = match.vlSource;
                if (targetName && targetName !== match.name) {
                  source = source.replace(/<Component-\w+/g, `<Component-${targetName}`);
                  source = source.replace(/<\/Component-\w+>/g, `</Component-${targetName}>`);
                }
                runtime.info?.('ComponentFetch fetched component from factory cache', {
                  name: targetName || match.name,
                });
                return {
                  result: {
                    type: 'vl_component',
                    name: targetName || match.name,
                    source,
                    format: '.cp',
                    fromFactory: true,
                    hint: 'Ready to use. Write to ExtComponents/ directory.',
                  }
                };
              }
            }

            // 2. Try by docId
            if (input.docId) {
              const res = await dcPost('SERVICE_DocCenter_GetDocById', { docId: input.docId });
              const doc = res?.data || res;
              const content = doc?.currentContent || '';
              if (!content) return { error: `Document ${input.docId} has no content.` };

              const isVLSource = content.includes('<Component-') && content.includes('# Frontend Tree');
              if (isVLSource) {
                let adapted = content;
                if (targetName) {
                  adapted = adapted.replace(/<Component-(\w+)/g, `<Component-${targetName}`);
                  adapted = adapted.replace(/<\/Component-(\w+)>/g, `</Component-${targetName}>`);
                }
                runtime.info?.('ComponentFetch fetched VL component from DocCenter', {
                  docId: input.docId,
                  name: targetName || doc.name?.replace(/^CP:\s*/, '') || 'UnknownComponent',
                });
                return {
                  result: {
                    type: 'vl_component',
                    name: targetName || doc.name?.replace(/^CP:\s*/, '') || 'UnknownComponent',
                    source: adapted,
                    format: '.cp',
                    fromDocCenter: true,
                    docId: input.docId,
                    hint: 'Ready to use. Write to ExtComponents/ directory.',
                  }
                };
              }

              runtime.info?.('ComponentFetch fetched component spec from DocCenter', {
                docId: input.docId,
                name: doc.name?.replace(/^CP:\s*/, '') || 'UnknownComponent',
              });
              return {
                result: {
                  type: 'component_spec',
                  name: doc.name?.replace(/^CP:\s*/, '') || 'UnknownComponent',
                  spec: content,
                  format: 'spec',
                  fromDocCenter: true,
                  docId: input.docId,
                  hint: 'This is a component spec (not VL source). Use LLM to generate .cp from this spec.',
                }
              };
            }

            runtime.warn?.('ComponentFetch fetch requires componentName or docId', {});
            return { error: 'Provide componentName or docId to fetch a component.' };
          }

          // ─── Search web for component patterns ───
          case 'webSearch': {
            const keyword = input.keyword || 'UI component';
            const category = input.category || '';
            const query = `${keyword} ${category} UI component specification props events API`.trim();

            // Search popular component library docs
            const sources = [
              { name: 'shadcn/ui', query: `site:ui.shadcn.com ${keyword}` },
              { name: 'Ant Design', query: `site:ant.design ${keyword} component API` },
              { name: 'MUI', query: `site:mui.com ${keyword} component API` },
              { name: 'Arco Design', query: `site:arco.design ${keyword} component` },
            ];

            // Use a simple web search approach
            const results = [];
            for (const src of sources) {
              try {
                const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(src.query)}&num=3`;
                // We can't do actual web search without a tool, so return the search URLs
                results.push({
                  source: src.name,
                  searchQuery: src.query,
                  suggestedUrls: getKnownComponentUrls(keyword, src.name),
                });
              } catch { /* skip */ }
            }

            runtime.info?.('ComponentFetch webSearch prepared candidates', {
              keyword,
              category: category || 'general',
              candidateSources: results.length,
            });
            return {
              result: {
                keyword,
                category: category || 'general',
                candidates: results,
                hint: 'Use webFetch with a specific URL to download component spec. The LLM will then adapt it to VL .cp format.',
              }
            };
          }

          // ─── Fetch component spec from web URL ───
          case 'webFetch': {
            if (!input.url) return { error: 'url required for webFetch' };

            try {
              const res = await fetch(input.url, {
                headers: { 'User-Agent': 'VL-Code-ComponentFetch/1.0' },
                signal: AbortSignal.timeout(15000),
              });
              const html = await res.text();

              // Extract useful content (strip HTML, keep text + code blocks)
              const extracted = extractComponentSpec(html, input.url);
              const targetName = input.componentName || guessComponentName(input.url);

              runtime.info?.('ComponentFetch fetched web component spec', {
                url: input.url,
                componentName: targetName,
              });
              return {
                result: {
                  type: 'web_component_spec',
                  name: targetName,
                  url: input.url,
                  source: extracted.source,
                  props: extracted.props,
                  events: extracted.events,
                  description: extracted.description,
                  hint: `Fetched ${targetName} spec from web. Use this as reference to generate VL .cp file. Key info: props, events, and visual structure.`,
                }
              };
            } catch (err) {
              runtime.error?.('ComponentFetch webFetch failed', {
                url: input.url,
                error: err.message,
              });
              return { error: `Failed to fetch ${input.url}: ${err.message}` };
            }
          }

          // ─── Publish component to DocCenter ───
          case 'publish': {
            if (!input.name || !input.content) return { error: 'name and content required for publish' };

            const docName = input.name.startsWith('CP:') ? input.name : `CP: ${input.name}`;
            const desc = input.description || `VL component: ${input.name}`;

            // Allocate path in the cp- range (200+)
            const listRes = await dcPost('SERVICE_DocCenter_GetDocList', { keyword: 'CP:', tagId: 0, page: 1, pageSize: 100 });
            const existingDocs = listRes?.data || [];
            const existingPaths = existingDocs.map(d => parseInt(d.path)).filter(n => !isNaN(n));
            let nextPath = 200;
            while (existingPaths.includes(nextPath)) nextPath++;

            // Check if component already exists (by name)
            const existing = existingDocs.find(d => d.name === docName);
            if (existing) {
              // Update existing
              const saveRes = await dcPost('SERVICE_DocCenter_SaveAsVersion', {
                path: existing.path,
                name: docName,
                description: desc,
                currentContent: input.content,
                changeNote: `Updated component ${input.name}`,
              });
              // Tag it
              const tagId = await ensureComponentTag();
              if (tagId && existing._id) {
                await dcPost('SERVICE_DocCenter_UpdateDocTags', { docId: existing._id, tagIds: [tagId] });
              }
              runtime.info?.('ComponentFetch updated component in DocCenter', {
                docId: existing._id,
                path: existing.path,
                name: docName,
              });
              return { result: { action: 'updated', docId: existing._id, path: existing.path, name: docName } };
            }

            // Create new
            const createRes = await dcPost('SERVICE_DocCenter_CreateDoc', {
              name: docName,
              description: desc,
              path: String(nextPath),
            });
            const newDoc = createRes?.data || createRes;
            const newDocId = newDoc?._id || newDoc?.id;

            if (newDocId) {
              // Save content
              await dcPost('SERVICE_DocCenter_SaveDoc', {
                docId: newDocId,
                name: docName,
                description: desc,
                currentContent: input.content,
              });
              // Tag it
              const tagId = await ensureComponentTag();
              if (tagId) {
                await dcPost('SERVICE_DocCenter_UpdateDocTags', { docId: newDocId, tagIds: [tagId] });
              }
            }

            return {
              result: {
                action: 'created',
                docId: newDocId,
                path: String(nextPath),
                name: docName,
                hint: 'Component published to DocCenter. Others can now search and fetch it.',
              }
            };
          }

          // ─── List all components in library ───
          case 'list': {
            const res = await dcPost('SERVICE_DocCenter_GetDocList', { keyword: 'CP:', tagId: 0, page: 1, pageSize: 100 });
            const docs = (res?.data || []).filter(d => d.name?.startsWith('CP:'));
            runtime.info?.('ComponentFetch listed component library', {
              count: docs.length,
            });
            return {
              result: {
                count: docs.length,
                components: docs.map(d => ({
                  docId: d._id,
                  name: d.name.replace(/^CP:\s*/, ''),
                  description: d.description,
                  path: d.path,
                })),
              }
            };
          }

          default:
            runtime.error?.('ComponentFetch action is unknown', {
              action: input.action,
            });
            return { error: `Unknown action: ${input.action}` };
        }
      } catch (err) {
        runtime.error?.('ComponentFetch crashed', {
          action: input.action,
          error: err.message,
        });
        return { error: `ComponentFetch error: ${err.message}` };
      }
    },
  };
}

// ─── Helper: known component URLs by library ───
function getKnownComponentUrls(keyword, source) {
  const kw = keyword.toLowerCase().replace(/\s+/g, '-');
  const mapping = {
    'shadcn/ui': `https://ui.shadcn.com/docs/components/${kw}`,
    'Ant Design': `https://ant.design/components/${kw}`,
    'MUI': `https://mui.com/material-ui/react-${kw}/`,
    'Arco Design': `https://arco.design/react/components/${kw}`,
  };
  return mapping[source] || null;
}

// ─── Helper: extract component spec from HTML ───
function extractComponentSpec(html, url) {
  // Strip HTML tags but keep structure
  let text = html
    .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
    .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
    .replace(/<nav[^>]*>[\s\S]*?<\/nav>/gi, '')
    .replace(/<footer[^>]*>[\s\S]*?<\/footer>/gi, '');

  // Extract code blocks (often contain API/props tables)
  const codeBlocks = [];
  const codeRegex = /<code[^>]*>([\s\S]*?)<\/code>/gi;
  let m;
  while ((m = codeRegex.exec(html)) !== null) {
    codeBlocks.push(m[1].replace(/<[^>]+>/g, ''));
  }

  // Extract table content (props tables)
  const tables = [];
  const tableRegex = /<table[^>]*>([\s\S]*?)<\/table>/gi;
  while ((m = tableRegex.exec(html)) !== null) {
    const cells = m[1].replace(/<[^>]+>/g, ' | ').replace(/\s+/g, ' ');
    tables.push(cells.trim());
  }

  // Clean text
  text = text.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();

  // Extract props-like patterns
  const props = [];
  const propsRegex = /(\w+)\s*[:|]\s*(string|number|boolean|function|object|array|ReactNode|any)/gi;
  while ((m = propsRegex.exec(text)) !== null) {
    props.push({ name: m[1], type: m[2] });
  }

  // Extract event-like patterns
  const events = [];
  const eventRegex = /(on[A-Z]\w+)\s*[:|]\s*\(([^)]*)\)/g;
  while ((m = eventRegex.exec(text)) !== null) {
    events.push({ name: m[1], params: m[2] });
  }

  // Truncate to reasonable size
  const maxLen = 8000;
  const source = text.length > maxLen ? text.substring(0, maxLen) + '...(truncated)' : text;

  // Description: first meaningful paragraph
  const descMatch = text.match(/^[\s\S]{0,500}?[.。]/);
  const description = descMatch ? descMatch[0].trim() : text.substring(0, 200);

  return { source, props, events, description, codeBlocks: codeBlocks.slice(0, 10), tables: tables.slice(0, 5) };
}

// ─── Helper: guess component name from URL ───
function guessComponentName(url) {
  try {
    const parts = new URL(url).pathname.split('/').filter(Boolean);
    const last = parts[parts.length - 1] || 'UnknownComponent';
    // Convert kebab-case to PascalCase
    return last.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('');
  } catch {
    return 'UnknownComponent';
  }
}
