/**
 * SiteAuditor Tests
 *
 * Tests for the comprehensive SEO site audit functionality including
 * technical SEO analysis, content quality assessment, site architecture evaluation,
 * performance monitoring, and accessibility compliance checking.
 *
 * @since 2.7.0
 */

import { describe, it, expect, beforeEach, vi } from "vitest";
import { SiteAuditor } from "../../../dist/tools/seo/auditors/SiteAuditor.js";

// Mock WordPress client
const createMockClient = () => ({
  getPosts: vi.fn(),
  getPages: vi.fn(),
  getSiteUrl: vi.fn().mockReturnValue("https://example.com"),
  authenticate: vi.fn().mockResolvedValue(true),
});

// Sample test data
const samplePosts = [
  {
    id: 1,
    title: { rendered: "Complete Guide to WordPress SEO Optimization" },
    content: {
      rendered: `
        <h1>Complete Guide to WordPress SEO Optimization</h1>
        <p>WordPress SEO optimization is crucial for improving your website's search engine visibility.
        This comprehensive guide covers all essential aspects of technical SEO, content optimization,
        and performance enhancement. Search engines like Google prioritize well-optimized WordPress sites
        that provide excellent user experience and valuable content to their audience.</p>
        <p>Understanding the fundamentals of WordPress SEO helps you create content that ranks higher
        in search results. This includes optimizing your content structure, using proper heading hierarchy,
        and ensuring your website loads quickly for both desktop and mobile users.</p>
        <p>Technical SEO considerations include proper URL structure, meta tags optimization, schema markup
        implementation, and ensuring your WordPress site is easily crawlable by search engine bots.
        Content optimization involves keyword research, proper keyword placement, and creating comprehensive
        content that addresses user search intent effectively.</p>
      `,
    },
    excerpt: {
      rendered: "Learn comprehensive WordPress SEO techniques to improve your search rankings and organic traffic.",
    },
    date: "2024-01-15T10:00:00Z",
    modified: "2024-06-15T14:30:00Z",
    status: "publish",
    featured_media: 123,
    link: "https://example.com/wordpress-seo-guide",
  },
  {
    id: 2,
    title: { rendered: "WordPress Performance Tips" },
    content: {
      rendered: `
        <h1>WordPress Performance Tips</h1>
        <p>Site performance optimization.</p>
      `,
    },
    excerpt: { rendered: "" }, // Missing meta description
    date: "2022-01-10T09:00:00Z", // Old content
    modified: "2022-01-10T09:00:00Z",
    status: "publish",
    featured_media: 0, // No featured image
    link: "https://example.com/wordpress-performance-optimization-tips-for-better-speed-and-user-experience",
  },
  {
    id: 3,
    title: { rendered: "WordPress Performance Tips" }, // Duplicate title
    content: {
      rendered: `
        <h1>Duplicate Title Post</h1>
        <h1>Another H1 Tag</h1>
        <p>This post has multiple H1 tags which is bad for SEO.</p>
        <img src="image1.jpg" alt="">
        <img src="image2.jpg">
      `,
    },
    excerpt: { rendered: "Short" }, // Too short meta description
    date: "2024-01-05T14:00:00Z",
    modified: "2024-01-05T14:00:00Z",
    status: "publish",
    featured_media: 456,
    link: "https://example.com/duplicate-title",
  },
];

const samplePages = [
  {
    id: 101,
    title: { rendered: "About Us" },
    content: {
      rendered: `
        <p>Short about page content that doesn't provide much value or information about the company.</p>
      `,
    },
    excerpt: { rendered: "Learn more about our company and mission statement for business success." },
    date: "2023-06-01T12:00:00Z",
    modified: "2024-01-15T10:00:00Z",
    status: "publish",
    featured_media: 789,
    link: "https://example.com/about",
  },
  {
    id: 102,
    title: { rendered: "Contact" },
    content: {
      rendered: `
        <h1>Contact Us</h1>
        <p>Get in touch with our team for any inquiries, support requests, or business opportunities.
        We are committed to providing excellent customer service and responding to all communications
        promptly. Our experienced team is here to help you achieve your goals and answer any questions
        you may have about our services, products, or solutions.</p>
        <p>Whether you need technical support, want to learn more about our offerings, or are interested
        in partnering with us, we welcome the opportunity to connect. Please use the contact form below
        or reach out directly using the contact information provided. We look forward to hearing from you
        and building a successful business relationship together.</p>
      `,
    },
    excerpt: {
      rendered: "Contact our team for support, inquiries, and business opportunities. We're here to help you succeed.",
    },
    date: "2024-02-01T15:30:00Z",
    modified: "2024-07-01T09:15:00Z",
    status: "publish",
    featured_media: 0, // Missing featured image
    link: "https://example.com/contact",
  },
];

describe("SiteAuditor", () => {
  let siteAuditor;
  let mockClient;

  beforeEach(() => {
    mockClient = createMockClient();

    // Mock successful API responses
    mockClient.getPosts.mockResolvedValue(samplePosts);
    mockClient.getPages.mockResolvedValue(samplePages);

    siteAuditor = new SiteAuditor(mockClient, {
      includeTechnical: true,
      includeContent: true,
      includeArchitecture: true,
      includePerformance: true,
      includeAccessibility: true,
      maxPagesForContentAudit: 50,
      minSeverityLevel: "low", // Include all issues for testing
      includeRecommendations: true,
    });
  });

  describe("Configuration", () => {
    it("should initialize with default configuration", () => {
      const defaultAuditor = new SiteAuditor(mockClient);
      const config = defaultAuditor.getConfig();

      expect(config.includeTechnical).toBe(true);
      expect(config.includeContent).toBe(true);
      expect(config.includeArchitecture).toBe(true);
      expect(config.includePerformance).toBe(true);
      expect(config.includeAccessibility).toBe(false);
      expect(config.maxPagesForContentAudit).toBe(50);
      expect(config.minSeverityLevel).toBe("medium");
      expect(config.includeRecommendations).toBe(true);
    });

    it("should allow configuration updates", () => {
      siteAuditor.updateConfig({
        maxPagesForContentAudit: 100,
        minSeverityLevel: "high",
        includeAccessibility: true,
      });

      const config = siteAuditor.getConfig();
      expect(config.maxPagesForContentAudit).toBe(100);
      expect(config.minSeverityLevel).toBe("high");
      expect(config.includeAccessibility).toBe(true);
    });
  });

  describe("Site Audit Execution", () => {
    it("should perform comprehensive site audit", async () => {
      const params = {
        site: "test",
      };

      const auditResult = await siteAuditor.performSiteAudit(params);

      expect(auditResult).toBeDefined();
      expect(auditResult.timestamp).toBeDefined();
      expect(auditResult.siteUrl).toBeDefined();
      expect(typeof auditResult.overallScore).toBe("number");
      expect(auditResult.overallScore).toBeGreaterThanOrEqual(0);
      expect(auditResult.overallScore).toBeLessThanOrEqual(100);

      expect(Array.isArray(auditResult.sections)).toBe(true);
      expect(auditResult.sections.length).toBeGreaterThan(0);

      expect(Array.isArray(auditResult.issues)).toBe(true);
      expect(Array.isArray(auditResult.recommendations)).toBe(true);
      expect(typeof auditResult.summary).toBe("string");
      expect(typeof auditResult.processingTime).toBe("number");
    });

    it("should identify technical SEO issues", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      const technicalSection = auditResult.sections.find((s) => s.name === "Technical SEO");
      expect(technicalSection).toBeDefined();

      // Should find issues like missing meta descriptions, duplicate titles, thin content
      const technicalIssues = auditResult.issues.filter((i) => i.category === "technical");
      expect(technicalIssues.length).toBeGreaterThan(0);

      // Check for specific expected issues
      const metaIssues = technicalIssues.find((i) => i.id === "missing-meta-descriptions");
      const duplicateIssues = technicalIssues.find((i) => i.id === "duplicate-titles");
      const thinContentIssues = technicalIssues.find((i) => i.id === "thin-content");

      expect(metaIssues || duplicateIssues || thinContentIssues).toBeDefined();
    });

    it("should identify content quality issues", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      const contentSection = auditResult.sections.find((s) => s.name === "Content Quality");
      expect(contentSection).toBeDefined();

      // Should find issues like outdated content, missing featured images
      const contentIssues = auditResult.issues.filter((i) => i.category === "content");
      expect(contentIssues.length).toBeGreaterThan(0);

      // Check for specific expected issues
      const outdatedIssues = contentIssues.find((i) => i.id === "outdated-content");
      const imageIssues = contentIssues.find((i) => i.id === "missing-featured-images");
      const readabilityIssues = contentIssues.find((i) => i.id === "poor-readability");

      expect(outdatedIssues || imageIssues || readabilityIssues).toBeDefined();
    });

    it("should identify site architecture issues", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      const architectureSection = auditResult.sections.find((s) => s.name === "Site Architecture");
      expect(architectureSection).toBeDefined();

      // Should identify potential architecture issues
      const architectureIssues = auditResult.issues.filter((i) => i.category === "architecture");

      // Check issue structure
      architectureIssues.forEach((issue) => {
        expect(issue).toHaveProperty("id");
        expect(issue).toHaveProperty("title");
        expect(issue).toHaveProperty("description");
        expect(issue).toHaveProperty("severity");
        expect(issue).toHaveProperty("category");
        expect(issue).toHaveProperty("affectedItems");
        expect(issue).toHaveProperty("impact");
        expect(["low", "medium", "high", "critical"]).toContain(issue.severity);
      });
    });

    it("should identify performance issues", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      const performanceSection = auditResult.sections.find((s) => s.name === "Performance");
      expect(performanceSection).toBeDefined();

      // Performance analysis may find optimization opportunities
      const performanceIssues = auditResult.issues.filter((i) => i.category === "performance");

      // Validate performance issue structure
      performanceIssues.forEach((issue) => {
        expect(issue.category).toBe("performance");
        expect(typeof issue.impact).toBe("string");
        expect(issue.impact.length).toBeGreaterThan(0);
      });
    });

    it("should identify accessibility issues", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      const accessibilitySection = auditResult.sections.find((s) => s.name === "Accessibility");
      expect(accessibilitySection).toBeDefined();

      // Should find missing alt text issues
      const accessibilityIssues = auditResult.issues.filter((i) => i.category === "accessibility");

      if (accessibilityIssues.length > 0) {
        const altTextIssues = accessibilityIssues.find((i) => i.id === "missing-alt-text");
        if (altTextIssues) {
          expect(altTextIssues.severity).toBe("high");
          expect(altTextIssues.impact).toContain("screen readers");
        }
      }
    });

    it("should filter issues by severity level", async () => {
      // Test with high severity filter
      siteAuditor.updateConfig({ minSeverityLevel: "high" });

      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      // All returned issues should be high or critical
      auditResult.issues.forEach((issue) => {
        expect(["high", "critical"]).toContain(issue.severity);
      });
    });

    it("should generate meaningful recommendations", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      expect(auditResult.recommendations.length).toBeGreaterThan(0);

      // Recommendations should be actionable
      auditResult.recommendations.forEach((recommendation) => {
        expect(typeof recommendation).toBe("string");
        expect(recommendation.length).toBeGreaterThan(20);
        // Should contain action words (more flexible regex)
        expect(
          /\b(add|improve|optimize|fix|update|review|implement|ensure|create|expand)\b/i.test(recommendation),
        ).toBe(true);
      });
    });

    it("should calculate overall score correctly", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      expect(auditResult.overallScore).toBeGreaterThanOrEqual(0);
      expect(auditResult.overallScore).toBeLessThanOrEqual(100);

      // Score should decrease with more critical issues
      const criticalIssues = auditResult.issues.filter((i) => i.severity === "critical").length;
      if (criticalIssues > 0) {
        expect(auditResult.overallScore).toBeLessThan(95); // Should be penalized
      }
    });

    it("should generate comprehensive audit summary", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      expect(auditResult.summary).toContain("audit completed");
      expect(auditResult.summary).toContain("sections");
      expect(auditResult.summary).toContain("issues");

      // Should mention issue counts
      const totalIssues = auditResult.issues.length;
      const criticalIssues = auditResult.issues.filter((i) => i.severity === "critical").length;

      if (totalIssues > 0) {
        expect(auditResult.summary).toContain(totalIssues.toString());
      }
      if (criticalIssues > 0) {
        expect(auditResult.summary).toContain(criticalIssues.toString());
      }
    });
  });

  describe("Selective Audit Sections", () => {
    it("should perform only technical audit when configured", async () => {
      siteAuditor.updateConfig({
        includeTechnical: true,
        includeContent: false,
        includeArchitecture: false,
        includePerformance: false,
        includeAccessibility: false,
      });

      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      expect(auditResult.sections.length).toBe(1);
      expect(auditResult.sections[0].name).toBe("Technical SEO");

      // All issues should be technical
      auditResult.issues.forEach((issue) => {
        expect(issue.category).toBe("technical");
      });
    });

    it("should skip recommendations when disabled", async () => {
      siteAuditor.updateConfig({ includeRecommendations: false });

      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      expect(auditResult.recommendations.length).toBe(0);
    });
  });

  describe("Error Handling", () => {
    it("should handle API errors gracefully", async () => {
      mockClient.getPosts.mockRejectedValue(new Error("API Error"));
      mockClient.getPages.mockRejectedValue(new Error("API Error"));

      const params = { site: "test" };

      await expect(siteAuditor.performSiteAudit(params)).rejects.toThrow("API Error");
    });

    it("should handle empty site data", async () => {
      mockClient.getPosts.mockResolvedValue([]);
      mockClient.getPages.mockResolvedValue([]);

      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      expect(auditResult).toBeDefined();
      expect(auditResult.overallScore).toBeGreaterThanOrEqual(0);

      // Should still have sections even with no content
      expect(auditResult.sections.length).toBeGreaterThan(0);
    });
  });

  describe("Performance", () => {
    it("should complete audit within reasonable time", async () => {
      const params = { site: "test" };

      const startTime = Date.now();
      const auditResult = await siteAuditor.performSiteAudit(params);
      const endTime = Date.now();

      expect(auditResult).toBeDefined();
      expect(endTime - startTime).toBeLessThan(10000); // Should complete within 10 seconds
      // Processing time should be reasonable (allow for very fast execution in tests)
      expect(auditResult.processingTime).toBeGreaterThanOrEqual(0);
      expect(auditResult.processingTime).toBeLessThan(10000);
    });

    it("should handle large content sets efficiently", async () => {
      // Create larger dataset
      const largePosts = Array.from({ length: 30 }, (_, i) => ({
        ...samplePosts[0],
        id: i + 100,
        title: { rendered: `Post ${i + 1}` },
        content: {
          rendered: `<h1>Post ${i + 1}</h1><p>Content for post ${i + 1} with sufficient length to analyze.</p>`.repeat(
            10,
          ),
        },
      }));

      mockClient.getPosts.mockResolvedValue(largePosts);

      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      expect(auditResult).toBeDefined();
      expect(auditResult.processingTime).toBeLessThan(15000); // Should handle larger datasets efficiently
    });
  });

  describe("External Link Detection", () => {
    it("should correctly distinguish internal from external links using hostname comparison", async () => {
      // Set up content with internal links, external links, and tricky subdomain cases
      const postsWithLinks = [
        {
          id: 1,
          title: { rendered: "Link Test Post" },
          content: {
            rendered: `
              <a href="https://example.com/about">Internal</a>
              <a href="https://example.com/blog/post">Internal path</a>
              <a href="https://notexample.com/page">External (substring match trap)</a>
              <a href="https://other.com/page">External</a>
              <a href="https://sub.example.com/page">External subdomain</a>
            `,
          },
          excerpt: { rendered: "Test" },
          slug: "link-test",
          status: "publish",
          date: "2024-01-01T00:00:00",
          modified: "2024-01-01T00:00:00",
          link: "https://example.com/link-test",
        },
      ];

      mockClient.getPosts.mockResolvedValue(postsWithLinks);
      mockClient.getPages.mockResolvedValue([]);

      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      // The audit should complete without errors and include performance section
      expect(auditResult).toBeDefined();
      expect(Array.isArray(auditResult.sections)).toBe(true);

      const perfSection = auditResult.sections.find((s) => s.name.toLowerCase().includes("performance"));
      expect(perfSection).toBeDefined();
    });
  });

  describe("Content Analysis", () => {
    it("should analyze content readability correctly", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      // Should identify readability issues if present
      const readabilityIssues = auditResult.issues.filter((i) => i.id === "poor-readability");

      readabilityIssues.forEach((issue) => {
        expect(issue.category).toBe("content");
        expect(issue.impact).toContain("readability");
        expect(issue.affectedItems.length).toBeGreaterThan(0);
      });
    });

    it("should detect missing alt text in images", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      // Sample data includes images without alt text
      const altTextIssues = auditResult.issues.filter((i) => i.id === "missing-alt-text");

      if (altTextIssues.length > 0) {
        expect(altTextIssues[0].category).toBe("accessibility");
        expect(altTextIssues[0].severity).toBe("high");
        expect(altTextIssues[0].impact).toContain("screen readers");
      }
    });

    it("completes audit without throwing when siteUrl is not a valid URL", async () => {
      // Triggers the URL-parse fallback + logger.warn in auditPerformance
      mockClient.getSiteUrl.mockReturnValue("not-a-valid-url");
      const params = { site: "test" };
      await expect(siteAuditor.performSiteAudit(params)).resolves.toBeDefined();
    });

    it("completes audit without throwing when content contains malformed URLs", async () => {
      // Malformed URLs (e.g. `http://[invalid`) cause new URL() to throw;
      // the auditor should count them and warn but not propagate the error.
      const postWithBadUrls = {
        id: 99,
        title: { rendered: "Bad URL Post" },
        content: {
          rendered: `<p>See <a href="http://[invalid">link</a> and https://external.com/ok</p>`,
        },
        excerpt: { rendered: "excerpt" },
        date: "2024-01-01T00:00:00Z",
        modified: "2024-01-01T00:00:00Z",
        status: "publish",
        featured_media: 0,
        link: "https://example.com/bad-url-post",
      };
      mockClient.getPosts.mockResolvedValue([postWithBadUrls]);
      mockClient.getPages.mockResolvedValue([]);
      const params = { site: "test" };
      await expect(siteAuditor.performSiteAudit(params)).resolves.toBeDefined();
    });

    it("should identify heading structure problems", async () => {
      const params = { site: "test" };
      const auditResult = await siteAuditor.performSiteAudit(params);

      // Sample data includes posts with multiple H1 tags
      const headingIssues = auditResult.issues.filter((i) => i.id === "heading-structure");

      if (headingIssues.length > 0) {
        expect(headingIssues[0].category).toBe("architecture");
        expect(headingIssues[0].impact).toContain("heading structure");
        expect(headingIssues[0].affectedItems.length).toBeGreaterThan(0);
      }
    });
  });
});
