/**
 * MCP Server for Competitor Research.
 *
 * Registers four tools:
 *   - start_research:      Opens the intake form UI
 *   - analyze_competitors:  Runs full analysis and returns dashboard data
 *   - refresh_competitor:   Re-researches a single competitor
 *   - export_report:        Exports the last result as markdown or JSON
 *
 * Also registers resources to serve the bundled intake-form and dashboard HTML.
 *
 * @module server
 */

import {
  registerAppResource,
  registerAppTool,
  RESOURCE_MIME_TYPE,
} from "@modelcontextprotocol/ext-apps/server";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type {
  CallToolResult,
  ReadResourceResult,
} from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { z } from "zod";

import { researchCompetitor, runFullAnalysis } from "./src/research/engine.js";
import type { FocusArea, ResearchResult } from "./src/types.js";

// ─── Resolve dist directory (works from source via tsx and compiled) ────────

const DIST_DIR = import.meta.filename.endsWith(".ts")
  ? path.join(import.meta.dirname, "dist")
  : import.meta.dirname;

// ─── Zod schemas for input validation ───────────────────────────────────────

const CompanyProfileSchema = z.object({
  name: z.string().describe("Company name"),
  url: z.string().url().describe("Company website URL"),
  industry: z.string().describe("Industry or market category"),
  description: z.string().describe("Brief company description"),
  valueProps: z.array(z.string()).describe("Key value propositions"),
});

const CompetitorEntrySchema = z.object({
  name: z.string().describe("Competitor name"),
  url: z.string().url().describe("Competitor website URL"),
  notes: z.string().optional().describe("Optional notes about this competitor"),
});

const FocusAreaSchema = z.enum(["pricing", "features", "content", "social", "seo", "all"]);

const ResearchBriefSchema = z.object({
  company: CompanyProfileSchema.describe("Your company profile"),
  competitors: z.array(CompetitorEntrySchema).min(1).describe("List of competitors to analyse"),
  focusAreas: z.array(FocusAreaSchema).min(1).describe("Aspects to focus the research on"),
});

// ─── Result cache (per server factory call — effectively per session) ──────

let lastResult: ResearchResult | null = null;

// ─── Helpers ────────────────────────────────────────────────────────────────

/**
 * Format a ResearchResult as a Markdown report.
 */
function formatMarkdown(result: ResearchResult): string {
  const lines: string[] = [];
  const { brief, analyses, opportunities, generatedAt } = result;

  lines.push(`# Competitor Research Report`);
  lines.push(`**Company:** ${brief.company.name}  `);
  lines.push(`**Industry:** ${brief.company.industry}  `);
  lines.push(`**Generated:** ${new Date(generatedAt).toLocaleString()}  `);
  lines.push("");

  lines.push("## Company Overview");
  lines.push(brief.company.description);
  lines.push("");
  lines.push("**Value Propositions:**");
  for (const vp of brief.company.valueProps) {
    lines.push(`- ${vp}`);
  }
  lines.push("");

  lines.push("---");
  lines.push("");

  for (const analysis of analyses) {
    lines.push(`## ${analysis.competitor.name}`);
    lines.push(`**URL:** ${analysis.competitor.url}  `);
    if (analysis.competitor.notes) {
      lines.push(`**Notes:** ${analysis.competitor.notes}  `);
    }
    lines.push("");

    if (analysis.features.length > 0) {
      lines.push("### Features");
      for (const f of analysis.features) {
        lines.push(`- ${f}`);
      }
      lines.push("");
    }

    if (analysis.pricing) {
      lines.push("### Pricing");
      for (const tier of analysis.pricing.tiers) {
        lines.push(`**${tier.name}** — ${tier.price}`);
        for (const f of tier.features) {
          lines.push(`  - ${f}`);
        }
      }
      lines.push("");
    }

    if (analysis.socialPresence && analysis.socialPresence.length > 0) {
      lines.push("### Social Presence");
      for (const sp of analysis.socialPresence) {
        lines.push(`- **${sp.platform}**${sp.followers ? ` (${sp.followers} followers)` : ""}`);
      }
      lines.push("");
    }

    if (analysis.contentStrategy) {
      lines.push("### Content Strategy");
      lines.push(`**Frequency:** ${analysis.contentStrategy.blogFrequency ?? "Unknown"}`);
      if (analysis.contentStrategy.topTopics.length > 0) {
        lines.push("**Top Topics:**");
        for (const t of analysis.contentStrategy.topTopics) {
          lines.push(`- ${t}`);
        }
      }
      lines.push("");
    }

    if (analysis.seoMetrics) {
      lines.push("### SEO Metrics");
      if (analysis.seoMetrics.topKeywords.length > 0) {
        lines.push(`**Keywords:** ${analysis.seoMetrics.topKeywords.join(", ")}`);
      }
      lines.push(`**Est. Traffic:** ${analysis.seoMetrics.estimatedTraffic ?? "Unknown"}`);
      lines.push(`**Domain Authority:** ${analysis.seoMetrics.domainAuthority ?? "Unknown"}`);
      lines.push("");
    }

    lines.push("### Strengths");
    for (const s of analysis.strengths) {
      lines.push(`- ✅ ${s}`);
    }
    lines.push("");

    lines.push("### Weaknesses");
    for (const w of analysis.weaknesses) {
      lines.push(`- ⚠️ ${w}`);
    }
    lines.push("");
    lines.push("---");
    lines.push("");
  }

  lines.push("## Opportunities");
  for (const opp of opportunities) {
    lines.push(`- 🎯 ${opp}`);
  }
  lines.push("");

  return lines.join("\n");
}

/**
 * Generate a concise text summary of the research result for model consumption.
 */
function textSummary(result: ResearchResult): string {
  const { analyses, opportunities } = result;
  const lines: string[] = [
    `Competitor Research Complete — ${analyses.length} competitor(s) analysed.`,
    "",
  ];

  for (const a of analyses) {
    lines.push(
      `• ${a.competitor.name}: ${a.features.length} features found, ` +
        `${a.strengths.length} strengths, ${a.weaknesses.length} weaknesses` +
        `${a.pricing ? `, ${a.pricing.tiers.length} pricing tier(s)` : ""}`,
    );
  }

  lines.push("");
  lines.push(`Top opportunities: ${opportunities.slice(0, 3).join("; ")}`);

  return lines.join("\n");
}

// ─── Server factory ─────────────────────────────────────────────────────────

/**
 * Creates a new MCP server instance with all tools and resources registered.
 *
 * Each call returns a fresh McpServer (required for stateless HTTP transport).
 */
export function createServer(): McpServer {
  const server = new McpServer({
    name: "Competitor Research MCP",
    version: "1.0.0",
  });

  // ─── Resource URIs ────────────────────────────────────────────────────────

  const intakeResourceUri = "ui://competitor-research/intake-form.html";
  const dashboardResourceUri = "ui://competitor-research/dashboard.html";

  // ─── Tool 1: start_research ───────────────────────────────────────────────

  registerAppTool(
    server,
    "start_research",
    {
      title: "Start Competitor Research",
      description:
        "Opens the research intake form where you can enter your company details and competitors to analyse.",
      inputSchema: {},
      _meta: { ui: { resourceUri: intakeResourceUri } },
    },
    async (): Promise<CallToolResult> => {
      return {
        content: [
          {
            type: "text",
            text: "I've opened the research intake form. Please fill in your company details and competitors, then submit the form to begin analysis.",
          },
        ],
      };
    },
  );

  // ─── Tool 2: analyze_competitors ──────────────────────────────────────────

  registerAppTool(
    server,
    "analyze_competitors",
    {
      title: "Analyse Competitors",
      description:
        "Runs a full competitive analysis based on the research brief. Scrapes competitor websites to extract pricing, features, content strategy, social presence, and SEO metrics.",
      inputSchema: ResearchBriefSchema,
      _meta: { ui: { resourceUri: dashboardResourceUri } },
    },
    async (brief): Promise<CallToolResult> => {
      try {
        const result = await runFullAnalysis(brief);

        // Cache for export_report
        lastResult = result;

        const summary = textSummary(result);

        return {
          content: [{ type: "text", text: summary }],
          structuredContent: result as unknown as Record<string, unknown>,
        };
      } catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        console.error("[server] analyze_competitors error:", message);
        return {
          content: [
            {
              type: "text",
              text: `Analysis encountered an error: ${message}. Partial results may be available.`,
            },
          ],
          isError: true,
        };
      }
    },
  );

  // ─── Tool 3: refresh_competitor ───────────────────────────────────────────

  server.registerTool(
    "refresh_competitor",
    {
      title: "Refresh Competitor",
      description: "Re-run research on a single competitor to get updated data.",
      inputSchema: {
        competitorUrl: z.string().url().describe("Competitor website URL to refresh"),
        focusAreas: z
          .array(FocusAreaSchema)
          .min(1)
          .default(["all"])
          .describe("Focus areas to research"),
      },
      annotations: {
        readOnlyHint: true,
        openWorldHint: true,
      },
    },
    async ({ competitorUrl, focusAreas }): Promise<CallToolResult> => {
      try {
        // Derive competitor name from URL
        let name: string;
        try {
          name = new URL(competitorUrl).hostname.replace(/^www\./, "");
        } catch {
          name = competitorUrl;
        }

        const analysis = await researchCompetitor(
          { name, url: competitorUrl },
          focusAreas as FocusArea[],
        );

        return {
          content: [
            {
              type: "text",
              text: `Refreshed analysis for ${name}: ${analysis.features.length} features, ${analysis.strengths.length} strengths, ${analysis.weaknesses.length} weaknesses.`,
            },
          ],
          structuredContent: analysis as unknown as Record<string, unknown>,
        };
      } catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return {
          content: [
            { type: "text", text: `Refresh failed: ${message}` },
          ],
          isError: true,
        };
      }
    },
  );

  // ─── Tool 4: export_report ────────────────────────────────────────────────

  server.registerTool(
    "export_report",
    {
      title: "Export Report",
      description:
        "Export the last competitor analysis as a formatted Markdown or JSON report.",
      inputSchema: {
        format: z
          .enum(["markdown", "json"])
          .default("markdown")
          .describe('Report format — "markdown" or "json"'),
      },
    },
    async ({ format }): Promise<CallToolResult> => {
      if (!lastResult) {
        return {
          content: [
            {
              type: "text",
              text: "No analysis results available yet. Run `analyze_competitors` first.",
            },
          ],
          isError: true,
        };
      }

      if (format === "json") {
        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(lastResult, null, 2),
            },
          ],
        };
      }

      return {
        content: [
          {
            type: "text",
            text: formatMarkdown(lastResult),
          },
        ],
      };
    },
  );

  // ─── Resources: serve bundled HTML apps ───────────────────────────────────

  registerAppResource(
    server,
    intakeResourceUri,
    intakeResourceUri,
    {
      mimeType: RESOURCE_MIME_TYPE,
      description: "Competitor Research Intake Form",
    },
    async (): Promise<ReadResourceResult> => {
      const html = await fs.readFile(
        path.join(DIST_DIR, "app-ui", "intake-form.html"),
        "utf-8",
      );
      return {
        contents: [
          { uri: intakeResourceUri, mimeType: RESOURCE_MIME_TYPE, text: html },
        ],
      };
    },
  );

  registerAppResource(
    server,
    dashboardResourceUri,
    dashboardResourceUri,
    {
      mimeType: RESOURCE_MIME_TYPE,
      description: "Competitor Research Dashboard",
    },
    async (): Promise<ReadResourceResult> => {
      const html = await fs.readFile(
        path.join(DIST_DIR, "app-ui", "dashboard.html"),
        "utf-8",
      );
      return {
        contents: [
          { uri: dashboardResourceUri, mimeType: RESOURCE_MIME_TYPE, text: html },
        ],
      };
    },
  );

  return server;
}
