<?php

namespace StaticDeploy\Local;

use StaticDeploy\DeployerTrait;
use StaticDeploy\FilesHelper;
use StaticDeploy\PathInfo;
use StaticDeploy\SiteInfo;
use StaticDeploy\WsLog;

class LocalDeployer {

    use DeployerTrait;

    const DEFAULT_NAMESPACE = 'static-deploy-addon-local/default';

    private int $deployed_ct = 0;

    private int $deploy_cache_ct = 0;

    private int $deploy_error_ct = 0;

    public static function getDeployerSlug(): string {
        return 'static-deploy-addon-local';
    }

    /**
     * @return array<string, string>
     */
    public static function getDeployerData(): array {
        return [
            'description' => 'Deploys to a local directory',
            'name' => 'Local Deployment',
            'url' => 'https://github.com/staticweb-io/static-deploy',
        ];
    }

    /**
     * @param \Iterator<PathInfo> $path_infos
     */
    public function uploadFilesIter( \Iterator $path_infos ): void {
        $dir_path = LocalOptions::getValue( 'dirPath' );
        // Make $out_dir absolute
        $out_dir = $dir_path === '' || $dir_path[0] !== '/'
        ? SiteInfo::getPath( 'site' ) . $dir_path
        : $dir_path;

        $out_dir = FilesHelper::normalizePath( $out_dir );
        if ( ! is_dir( $out_dir ) && ! FilesHelper::createDir( $out_dir ) ) {
            $msg = 'Failed to create local deployment directory: ' . $out_dir;
            throw WsLog::ex( esc_html( $msg ) );
        }
        $out_dir = realpath( $out_dir );
        $out_dir = trailingslashit( $out_dir );

        // Deployment inside the WP path is just too
        // error-prone to allow.
        // It's very easy to overwrite WordPress files
        // or to have files duplicated by crawling the
        // deployment directory.
        $site_dir = SiteInfo::getPath( 'site' );
        $site_dir = trailingslashit( realpath( $site_dir ) );

        if ( STATIC_DEPLOY_DEBUG ) {
            WsLog::d( 'Site dir: ' . $site_dir );
        }

        if ( mb_strpos( $out_dir, $site_dir ) === 0 ) {
            $msg = 'Local deployment directory must be outside of the WordPress directory: '
                . $out_dir;
            throw WsLog::ex( esc_html( $msg ) );
        }

        WsLog::l( 'Deploying to ' . $out_dir );

        $last_log_time = microtime( true );

        foreach ( $path_infos as $path_info ) {
            $now = microtime( true );
            $total = $this->deployed_ct + $this->deploy_cache_ct + $this->deploy_error_ct;
            if ( $total > 0 && $now - $last_log_time >= 60 ) {
                WsLog::l( 'Deployed ' . $path_info->path );
                $notice = "Deploy progress: $this->deployed_ct deployed," .
                    " $this->deploy_error_ct failed," .
                    " $this->deploy_cache_ct skipped (cached).";
                WsLog::l( $notice );
                $last_log_time = microtime( true );
            }

            $out_path = FilesHelper::getFilePath(
                $out_dir,
                $path_info->path,
            );

            // Remove 404s
            if ( $path_info->status === 404 ) {
                if ( is_file( $out_path ) ) {
                    FilesHelper::deleteFile( $out_path );
                }
                ++$this->deployed_ct;
                continue;
            }

            // Write file contents
            if ( $path_info->body || $path_info->filename ) {
                try {
                    FilesHelper::writePathInfo(
                        $out_dir,
                        $path_info,
                    );
                    ++$this->deployed_ct;
                } catch ( \Exception $e ) {
                    WsLog::w(
                        'Failed to deploy ' . $path_info->path .
                        ': ' . $e->getMessage()
                    );
                    ++$this->deploy_error_ct;
                }
            } else {
                WsLog::w( 'No content to write for ' . $path_info->path );
                ++$this->deploy_error_ct;
            }
        }

        $notice = "Deployed $this->deployed_ct files," .
            " $this->deploy_error_ct failed," .
            " $this->deploy_cache_ct skipped (cached).";
        WsLog::l( $notice );
    }
}
