<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

if ( ! class_exists( 'MYAIO_SEO_Snapshot' ) ) {

/**
 * MYAIO_SEO_Snapshot
 *
 * Builds a "full SEO snapshot":
 * - WP settings
 * - SEO plugin status + key options
 * - robots.txt + sitemap(s)
 * - all public URLs (posts/pages/CPT + taxonomies)
 *
 * Output: JSON files written into storage/snapshots/<run_id>/
 */
class MYAIO_SEO_Snapshot {

    /**
     * Create snapshot for a run.
     *
     * @param string $base_snapshots_dir Absolute path to storage/snapshots/
     * @param string $run_id
     * @param array  $opts Optional tuning.
     * @return array { dir, snapshot_file, urls_file, meta_file, url_count }
     * @throws Exception
     */
    public static function run( $base_snapshots_dir, $run_id, array $opts = array() ) {
        $base_snapshots_dir = (string) $base_snapshots_dir;
        $run_id             = (string) $run_id;

        if ( ! is_dir( $base_snapshots_dir ) && ! wp_mkdir_p( $base_snapshots_dir ) ) {
            throw new Exception( 'Cannot create snapshots directory.' );
        }
        if ( ! wp_is_writable( $base_snapshots_dir ) ) {
            throw new Exception( 'Snapshots directory is not writable.' );
        }

        $safe_run = sanitize_file_name( $run_id );
        if ( '' === $safe_run ) {
            $safe_run = 'run';
        }

        $run_dir = trailingslashit( $base_snapshots_dir ) . $safe_run . '/';
        if ( ! is_dir( $run_dir ) && ! wp_mkdir_p( $run_dir ) ) {
            throw new Exception( 'Cannot create run snapshot directory.' );
        }
        if ( ! wp_is_writable( $run_dir ) ) {
            throw new Exception( 'Run snapshot directory is not writable.' );
        }

        $defaults = array(
            'max_urls'            => 50000, // safety cap
            'include_taxonomies'  => true,
            'include_archives'    => true,
            'include_home'        => true,
        );
        $opts = array_merge( $defaults, $opts );

        $opts['max_urls'] = is_numeric( $opts['max_urls'] ) ? (int) $opts['max_urls'] : 50000;
        if ( $opts['max_urls'] < 0 ) {
            $opts['max_urls'] = 0;
        }

        $data = array();
        $data['generatedAt']  = gmdate( 'c' );
        $data['site']         = self::collect_wp_basics();
        $data['wp_settings']  = self::collect_wp_settings();
        $data['seo_plugins']  = self::collect_seo_plugins();
        $data['robots']       = self::collect_robots();
        $data['sitemaps']     = self::discover_sitemaps( $data['seo_plugins'] );

        // URL inventory + per-url SEO fields
        $urls = self::collect_all_public_urls( $opts );

        // Deduplicate URLs efficiently (avoid array_unique on huge arrays).
        $set  = array();
        $uniq = array();
        foreach ( (array) $urls as $u ) {
            $u = is_string( $u ) ? trim( $u ) : '';
            if ( '' === $u ) {
                continue;
            }
            $u = esc_url_raw( $u );
            if ( '' === $u ) {
                continue;
            }
            if ( isset( $set[ $u ] ) ) {
                continue;
            }
            $set[ $u ] = 1;
            $uniq[]    = $u;

            if ( $opts['max_urls'] > 0 && count( $uniq ) >= $opts['max_urls'] ) {
                $data['warnings'][] = 'URL list truncated to max_urls=' . (int) $opts['max_urls'];
                break;
            }
        }
        $urls = $uniq;

        $per_url = self::collect_per_url_seo( $urls, $data['seo_plugins'] );

        // Write files
        $snapshot_file = $run_dir . 'seo_snapshot.json';
        $urls_file     = $run_dir . 'urls.json';
        $meta_file     = $run_dir . 'url_meta.json';

        self::write_json_or_throw( $snapshot_file, $data );
        self::write_json_or_throw( $urls_file, $urls );
        self::write_json_or_throw( $meta_file, $per_url );

        return array(
            'dir'           => $run_dir,
            'snapshot_file' => $snapshot_file,
            'urls_file'     => $urls_file,
            'meta_file'     => $meta_file,
            'url_count'     => count( $urls ),
        );
    }

    private static function write_json_or_throw( $path, $data ) {
        $json = wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
        if ( false === $json ) {
            throw new Exception( 'Failed to encode snapshot JSON.' );
        }

        $bytes = @file_put_contents( $path, $json );
        if ( false === $bytes ) {
            throw new Exception( 'Failed to write snapshot file.' );
        }
    }

    private static function collect_wp_basics() {
        return array(
            'home'        => home_url( '/' ),
            'siteurl'     => site_url( '/' ),
            'name'        => get_bloginfo( 'name' ),
            'description' => get_bloginfo( 'description' ),
            'language'    => get_bloginfo( 'language' ),
            'wp_version'  => get_bloginfo( 'version' ),
            'php_version' => PHP_VERSION,
        );
    }

    private static function collect_wp_settings() {
        return array(
            'permalink_structure' => (string) get_option( 'permalink_structure', '' ),
            'blog_public'         => (int) get_option( 'blog_public', 1 ),
            'timezone_string'     => (string) get_option( 'timezone_string', '' ),
            'gmt_offset'          => (string) get_option( 'gmt_offset', '' ),
            'page_on_front'       => (int) get_option( 'page_on_front', 0 ),
            'page_for_posts'      => (int) get_option( 'page_for_posts', 0 ),
        );
    }

    private static function collect_seo_plugins() {
        if ( ! function_exists( 'is_plugin_active' ) ) {
            include_once ABSPATH . 'wp-admin/includes/plugin.php';
        }

        $known = array(
            'wordpress-seo/wp-seo.php'                => 'yoast',
            'rank-math/rank-math.php'                 => 'rankmath',
            'all-in-one-seo-pack/all_in_one_seo_pack.php' => 'aioseo',
            'autodescription/autodescription.php'     => 'seoframework',
        );

        $active = array();

        foreach ( $known as $path => $slug ) {
            if ( is_plugin_active( $path ) ) {
                $active[ $slug ] = array(
                    'plugin'  => $path,
                    'slug'    => $slug,
                    'active'  => true,
                    'options' => self::collect_plugin_options( $slug ),
                );
            }
        }

        return $active;
    }

    private static function collect_plugin_options( $slug ) {
        // Keep lightweight; avoid dumping huge sensitive options.
        switch ( (string) $slug ) {
            case 'yoast':
                return array(
                    'wpseo_titles' => get_option( 'wpseo_titles', null ),
                    'wpseo_social' => get_option( 'wpseo_social', null ),
                    'wpseo_xml'    => get_option( 'wpseo_xml', null ),
                );
            case 'rankmath':
                return array(
                    'rank_math_options_titles'   => get_option( 'rank_math_options_titles', null ),
                    'rank_math_options_general'  => get_option( 'rank_math_options_general', null ),
                    'rank_math_options_sitemap'  => get_option( 'rank_math_options_sitemap', null ),
                );
            case 'aioseo':
                return array(
                    'aioseo_options' => get_option( 'aioseo_options', null ),
                );
            case 'seoframework':
                return array(
                    'the_seo_framework_settings' => get_option( 'the_seo_framework_settings', null ),
                );
            default:
                return null;
        }
    }

    private static function http_get_text( $url, $timeout = 15 ) {
        $args = array(
            'timeout'     => (int) $timeout,
            'redirection' => 3,
            'user-agent'  => 'MYAIO-SEO-Snapshot/1.0; ' . home_url( '/' ),
        );

        $res = wp_remote_get( $url, $args );
        if ( is_wp_error( $res ) ) {
            return null;
        }

        $code = (int) wp_remote_retrieve_response_code( $res );
        if ( $code < 200 || $code >= 300 ) {
            return null;
        }

        return (string) wp_remote_retrieve_body( $res );
    }

    private static function collect_robots() {
        $robots = array(
            'virtual_robots_txt'  => null,
            'physical_robots_txt' => null,
        );

        $body = self::http_get_text( home_url( '/robots.txt' ), 15 );
        if ( null !== $body ) {
            $robots['virtual_robots_txt'] = $body;
        }

        $physical = ABSPATH . 'robots.txt';
        if ( file_exists( $physical ) && is_readable( $physical ) ) {
            // Best-effort; ok in snapshot context.
            $robots['physical_robots_txt'] = (string) @file_get_contents( $physical );
        }

        return $robots;
    }

    private static function discover_sitemaps( array $seo_plugins ) {
        $candidates = array(
            home_url( '/sitemap.xml' ),
            home_url( '/sitemap_index.xml' ),
            home_url( '/wp-sitemap.xml' ),
        );

        if ( ! empty( $seo_plugins['yoast'] ) ) {
            $candidates[] = home_url( '/sitemap_index.xml' );
        }
        if ( ! empty( $seo_plugins['rankmath'] ) ) {
            $candidates[] = home_url( '/sitemap_index.xml' );
        }
        if ( ! empty( $seo_plugins['aioseo'] ) ) {
            $candidates[] = home_url( '/sitemap.xml' );
            $candidates[] = home_url( '/sitemap_index.xml' );
        }

        $found = array();

        foreach ( array_unique( $candidates ) as $url ) {
            $url = esc_url_raw( (string) $url );
            if ( '' === $url ) {
                continue;
            }

            $body = self::http_get_text( $url, 15 );
            if ( null === $body ) {
                continue;
            }

            // Basic check it resembles an XML sitemap.
            if ( false !== stripos( $body, '<sitemapindex' ) || false !== stripos( $body, '<urlset' ) ) {
                $found[] = array(
                    'url'    => $url,
                    'status' => 200,
                    'bytes'  => strlen( $body ),
                );
            }
        }

        return $found;
    }

    private static function collect_all_public_urls( array $opts ) {
        $urls = array();

        if ( ! empty( $opts['include_home'] ) ) {
            $urls[] = home_url( '/' );
        }

        // Posts/pages + public CPTs.
        $post_types = get_post_types( array( 'public' => true ), 'names' );
        foreach ( (array) $post_types as $pt ) {
            if ( 'attachment' === $pt ) {
                continue;
            }

            $paged    = 1;
            $per_page = 500;

            do {
                $q = new WP_Query( array(
                    'post_type'      => $pt,
                    'post_status'    => 'publish',
                    'posts_per_page' => $per_page,
                    'paged'          => $paged,
                    'fields'         => 'ids',
                    'no_found_rows'  => true,
                ) );

                foreach ( (array) $q->posts as $post_id ) {
                    $u = get_permalink( (int) $post_id );
                    if ( $u ) {
                        $urls[] = $u;
                    }
                }

                $count = count( (array) $q->posts );
                $paged++;
            } while ( $count === $per_page );
        }

        // Taxonomy term archives (paged).
        if ( ! empty( $opts['include_taxonomies'] ) ) {
            $taxonomies = get_taxonomies( array( 'public' => true ), 'names' );

            foreach ( (array) $taxonomies as $tax ) {
                $offset = 0;
                $number = 500;

                do {
                    $terms = get_terms( array(
                        'taxonomy'   => $tax,
                        'hide_empty' => false,
                        'fields'     => 'ids',
                        'number'     => $number,
                        'offset'     => $offset,
                    ) );

                    if ( is_wp_error( $terms ) || empty( $terms ) ) {
                        break;
                    }

                    foreach ( (array) $terms as $term_id ) {
                        $u = get_term_link( (int) $term_id, $tax );
                        if ( ! is_wp_error( $u ) && $u ) {
                            $urls[] = $u;
                        }
                    }

                    $count  = count( (array) $terms );
                    $offset += $number;
                } while ( $count === $number );
            }
        }

        // Blog posts page archive.
        if ( ! empty( $opts['include_archives'] ) ) {
            $page_for_posts = (int) get_option( 'page_for_posts', 0 );
            if ( $page_for_posts ) {
                $u = get_permalink( $page_for_posts );
                if ( $u ) {
                    $urls[] = $u;
                }
            }
        }

        return $urls;
    }

    private static function collect_per_url_seo( array $urls, array $seo_plugins ) {
        $out = array();

        foreach ( (array) $urls as $url ) {
            $url = is_string( $url ) ? esc_url_raw( trim( $url ) ) : '';
            if ( '' === $url ) {
                continue;
            }

            $row = array(
                'url'              => $url,
                'type'             => 'unknown',
                'title'            => null,
                'meta_description' => null,
                'canonical'        => null,
            );

            $post_id = url_to_postid( $url );
            if ( $post_id ) {
                $row['type']  = 'post';
                $row['title'] = sanitize_text_field( (string) get_the_title( (int) $post_id ) );

                // Yoast meta
                $yoast_desc  = get_post_meta( $post_id, '_yoast_wpseo_metadesc', true );
                $yoast_canon = get_post_meta( $post_id, '_yoast_wpseo_canonical', true );

                // RankMath meta
                $rm_desc  = get_post_meta( $post_id, 'rank_math_description', true );
                $rm_canon = get_post_meta( $post_id, 'rank_math_canonical_url', true );

                // AIOSEO meta
                $aioseo_desc  = get_post_meta( $post_id, '_aioseo_description', true );
                $aioseo_canon = get_post_meta( $post_id, '_aioseo_canonical_url', true );

                $desc = $yoast_desc ? $yoast_desc : ( $rm_desc ? $rm_desc : ( $aioseo_desc ? $aioseo_desc : null ) );
                $can  = $yoast_canon ? $yoast_canon : ( $rm_canon ? $rm_canon : ( $aioseo_canon ? $aioseo_canon : null ) );

                if ( is_string( $desc ) && '' !== trim( $desc ) ) {
                    $row['meta_description'] = sanitize_textarea_field( wp_unslash( $desc ) );
                }

                if ( is_string( $can ) && '' !== trim( $can ) ) {
                    $row['canonical'] = esc_url_raw( wp_unslash( $can ) );
                }
            }

            $out[] = $row;
        }

        return $out;
    }
}

}
