import { Inject, Injectable } from '@nestjs/common';
import { eq, sql } from 'drizzle-orm';
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import * as schema from '../drizzle/schema';
import { PG_CONNECTION } from '../drizzle/schema';

interface SitemapUrl {
  loc: string;
  lastmod?: string;
  changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
  priority?: number;
}

interface SitemapIndexEntry {
  loc: string;
  lastmod?: string;
}

@Injectable()
export class SitemapService {
  private readonly baseUrl = 'https://www.bizmatch.net';
  private readonly URLS_PER_SITEMAP = 10000; // Google best practice

  constructor(@Inject(PG_CONNECTION) private readonly db: NodePgDatabase<typeof schema>) { }

  /**
   * Generate sitemap index (main sitemap.xml)
   * Lists all sitemap files: static, business-1, business-2, commercial-1, etc.
   */
  async generateSitemapIndex(): Promise<string> {
    const sitemaps: SitemapIndexEntry[] = [];

    // Add static pages sitemap
    sitemaps.push({
      loc: `${this.baseUrl}/bizmatch/sitemap/static.xml`,
      lastmod: this.formatDate(new Date()),
    });

    // Count business listings
    const businessCount = await this.getBusinessListingsCount();
    const businessPages = Math.ceil(businessCount / this.URLS_PER_SITEMAP) || 1;
    for (let page = 1; page <= businessPages; page++) {
      sitemaps.push({
        loc: `${this.baseUrl}/bizmatch/sitemap/business-${page}.xml`,
        lastmod: this.formatDate(new Date()),
      });
    }

    // Count commercial property listings
    const commercialCount = await this.getCommercialPropertiesCount();
    const commercialPages = Math.ceil(commercialCount / this.URLS_PER_SITEMAP) || 1;
    for (let page = 1; page <= commercialPages; page++) {
      sitemaps.push({
        loc: `${this.baseUrl}/bizmatch/sitemap/commercial-${page}.xml`,
        lastmod: this.formatDate(new Date()),
      });
    }

    // Count broker profiles
    const brokerCount = await this.getBrokerProfilesCount();
    const brokerPages = Math.ceil(brokerCount / this.URLS_PER_SITEMAP) || 1;
    for (let page = 1; page <= brokerPages; page++) {
      sitemaps.push({
        loc: `${this.baseUrl}/bizmatch/sitemap/brokers-${page}.xml`,
        lastmod: this.formatDate(new Date()),
      });
    }

    return this.buildXmlSitemapIndex(sitemaps);
  }

  /**
   * Generate static pages sitemap
   */
  async generateStaticSitemap(): Promise<string> {
    const urls = this.getStaticPageUrls();
    return this.buildXmlSitemap(urls);
  }

  /**
   * Generate business listings sitemap (paginated)
   */
  async generateBusinessSitemap(page: number): Promise<string> {
    const offset = (page - 1) * this.URLS_PER_SITEMAP;
    const urls = await this.getBusinessListingUrls(offset, this.URLS_PER_SITEMAP);
    return this.buildXmlSitemap(urls);
  }

  /**
   * Generate commercial property sitemap (paginated)
   */
  async generateCommercialSitemap(page: number): Promise<string> {
    const offset = (page - 1) * this.URLS_PER_SITEMAP;
    const urls = await this.getCommercialPropertyUrls(offset, this.URLS_PER_SITEMAP);
    return this.buildXmlSitemap(urls);
  }

  /**
   * Build XML sitemap index
   */
  private buildXmlSitemapIndex(sitemaps: SitemapIndexEntry[]): string {
    const sitemapElements = sitemaps
      .map(sitemap => {
        let element = `  <sitemap>\n    <loc>${sitemap.loc}</loc>`;
        if (sitemap.lastmod) {
          element += `\n    <lastmod>${sitemap.lastmod}</lastmod>`;
        }
        element += '\n  </sitemap>';
        return element;
      })
      .join('\n');

    return `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${sitemapElements}
</sitemapindex>`;
  }

  /**
   * Build XML sitemap string
   */
  private buildXmlSitemap(urls: SitemapUrl[]): string {
    const urlElements = urls.map(url => this.buildUrlElement(url)).join('\n  ');

    return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  ${urlElements}
</urlset>`;
  }

  /**
   * Build single URL element
   */
  private buildUrlElement(url: SitemapUrl): string {
    let element = `<url>\n    <loc>${url.loc}</loc>`;

    if (url.lastmod) {
      element += `\n    <lastmod>${url.lastmod}</lastmod>`;
    }

    if (url.changefreq) {
      element += `\n    <changefreq>${url.changefreq}</changefreq>`;
    }

    if (url.priority !== undefined) {
      element += `\n    <priority>${url.priority.toFixed(1)}</priority>`;
    }

    element += '\n  </url>';
    return element;
  }

  /**
   * Get static page URLs
   */
  private getStaticPageUrls(): SitemapUrl[] {
    return [
      {
        loc: `${this.baseUrl}/`,
        changefreq: 'daily',
        priority: 1.0,
      },
      {
        loc: `${this.baseUrl}/home`,
        changefreq: 'daily',
        priority: 1.0,
      },
      {
        loc: `${this.baseUrl}/businessListings`,
        changefreq: 'daily',
        priority: 0.9,
      },
      {
        loc: `${this.baseUrl}/commercialPropertyListings`,
        changefreq: 'daily',
        priority: 0.9,
      },
      {
        loc: `${this.baseUrl}/brokerListings`,
        changefreq: 'daily',
        priority: 0.8,
      },
      {
        loc: `${this.baseUrl}/terms-of-use`,
        changefreq: 'monthly',
        priority: 0.5,
      },
      {
        loc: `${this.baseUrl}/privacy-statement`,
        changefreq: 'monthly',
        priority: 0.5,
      },
    ];
  }

  /**
   * Count business listings (non-draft)
   */
  private async getBusinessListingsCount(): Promise<number> {
    try {
      const result = await this.db
        .select({ count: sql<number>`count(*)` })
        .from(schema.businesses_json)
        .where(sql`(${schema.businesses_json.data}->>'draft')::boolean IS NOT TRUE`);

      return Number(result[0]?.count || 0);
    } catch (error) {
      console.error('Error counting business listings:', error);
      return 0;
    }
  }

  /**
   * Count commercial properties (non-draft)
   */
  private async getCommercialPropertiesCount(): Promise<number> {
    try {
      const result = await this.db
        .select({ count: sql<number>`count(*)` })
        .from(schema.commercials_json)
        .where(sql`(${schema.commercials_json.data}->>'draft')::boolean IS NOT TRUE`);

      return Number(result[0]?.count || 0);
    } catch (error) {
      console.error('Error counting commercial properties:', error);
      return 0;
    }
  }

  /**
   * Get business listing URLs from database (paginated, slug-based)
   */
  private async getBusinessListingUrls(offset: number, limit: number): Promise<SitemapUrl[]> {
    try {
      const listings = await this.db
        .select({
          id: schema.businesses_json.id,
          slug: sql<string>`${schema.businesses_json.data}->>'slug'`,
          updated: sql<Date>`(${schema.businesses_json.data}->>'updated')::timestamptz`,
          created: sql<Date>`(${schema.businesses_json.data}->>'created')::timestamptz`,
        })
        .from(schema.businesses_json)
        .where(sql`(${schema.businesses_json.data}->>'draft')::boolean IS NOT TRUE`)
        .limit(limit)
        .offset(offset);

      return listings.map(listing => {
        const urlSlug = listing.slug || listing.id;
        return {
          loc: `${this.baseUrl}/business/${urlSlug}`,
          lastmod: this.formatDate(listing.updated || listing.created),
          changefreq: 'weekly' as const,
          priority: 0.8,
        };
      });
    } catch (error) {
      console.error('Error fetching business listings for sitemap:', error);
      return [];
    }
  }

  /**
   * Get commercial property URLs from database (paginated, slug-based)
   */
  private async getCommercialPropertyUrls(offset: number, limit: number): Promise<SitemapUrl[]> {
    try {
      const properties = await this.db
        .select({
          id: schema.commercials_json.id,
          slug: sql<string>`${schema.commercials_json.data}->>'slug'`,
          updated: sql<Date>`(${schema.commercials_json.data}->>'updated')::timestamptz`,
          created: sql<Date>`(${schema.commercials_json.data}->>'created')::timestamptz`,
        })
        .from(schema.commercials_json)
        .where(sql`(${schema.commercials_json.data}->>'draft')::boolean IS NOT TRUE`)
        .limit(limit)
        .offset(offset);

      return properties.map(property => {
        const urlSlug = property.slug || property.id;
        return {
          loc: `${this.baseUrl}/commercial-property/${urlSlug}`,
          lastmod: this.formatDate(property.updated || property.created),
          changefreq: 'weekly' as const,
          priority: 0.8,
        };
      });
    } catch (error) {
      console.error('Error fetching commercial properties for sitemap:', error);
      return [];
    }
  }

  /**
   * Format date to ISO 8601 format (YYYY-MM-DD)
   */
  private formatDate(date: Date | string): string {
    if (!date) return new Date().toISOString().split('T')[0];
    const d = typeof date === 'string' ? new Date(date) : date;
    return d.toISOString().split('T')[0];
  }

  /**
   * Generate broker profiles sitemap (paginated)
   */
  async generateBrokerSitemap(page: number): Promise<string> {
    const offset = (page - 1) * this.URLS_PER_SITEMAP;
    const urls = await this.getBrokerProfileUrls(offset, this.URLS_PER_SITEMAP);
    return this.buildXmlSitemap(urls);
  }

  /**
   * Count broker profiles (professionals with showInDirectory=true)
   */
  private async getBrokerProfilesCount(): Promise<number> {
    try {
      const result = await this.db
        .select({ count: sql<number>`count(*)` })
        .from(schema.users_json)
        .where(sql`
          (${schema.users_json.data}->>'customerType') = 'professional'
          AND (${schema.users_json.data}->>'showInDirectory')::boolean IS TRUE
        `);

      return Number(result[0]?.count || 0);
    } catch (error) {
      console.error('Error counting broker profiles:', error);
      return 0;
    }
  }

  /**
   * Get broker profile URLs from database (paginated)
   */
  private async getBrokerProfileUrls(offset: number, limit: number): Promise<SitemapUrl[]> {
    try {
      const brokers = await this.db
        .select({
          email: schema.users_json.email,
          updated: sql<Date>`(${schema.users_json.data}->>'updated')::timestamptz`,
          created: sql<Date>`(${schema.users_json.data}->>'created')::timestamptz`,
        })
        .from(schema.users_json)
        .where(sql`
          (${schema.users_json.data}->>'customerType') = 'professional'
          AND (${schema.users_json.data}->>'showInDirectory')::boolean IS TRUE
        `)
        .limit(limit)
        .offset(offset);

      return brokers.map(broker => ({
        loc: `${this.baseUrl}/details-user/${encodeURIComponent(broker.email)}`,
        lastmod: this.formatDate(broker.updated || broker.created),
        changefreq: 'weekly' as const,
        priority: 0.7,
      }));
    } catch (error) {
      console.error('Error fetching broker profiles for sitemap:', error);
      return [];
    }
  }
}
