<?php

namespace App\Console\Commands;

use App\Models\MigrationUserPlan;
use App\Services\XenforoApiService;
use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

#[Signature('migrate:users:execute {--limit=} {--wp-uploads-path=} {--xf-data-path=}')]
#[Description("Migrate all users in migration table to XenForo.")]
class MigrateUsersExecute extends Command
{

    private function extractWpRole(?string $serialized): string
    {
        $caps = $serialized ? @unserialize($serialized) : null;
        return is_array($caps) ? (array_key_first($caps) ?: 'contributor') : 'contributor';
    }

    private function extractWpAvatarPath(?string $serialized, string $wpUploadsPath ): ?string
    {
        $data = $serialized ? @unserialize($serialized) : null;
        $mediaId = $data['media_id'] ?? null;
        if( !$mediaId )
            return null;

        $relative = DB::connection('old_wp')
            ->table('postmeta')
            ->where('post_id', $mediaId)
            ->where('meta_key', '_wp_attached_file')
            ->value('meta_value');

        if( !$relative )
            return null;

        $path = rtrim($wpUploadsPath, '/') . '/' . $relative;
        return is_file($path) ? $path : null;
    }

    private function extractXfAvatarPath(int $userId, string $xfDataPath, string $size = 'o' ): ?string
    {
        $path = sprintf('%s/avatars/%s/%d/%d.jpg',
            rtrim($xfDataPath, '/'), $size, (int) floor($userId / 1000), $userId
        );
        return is_file($path) ? $path : null;
    }

    private function extractXfBannerPath(int $userId, string $xfDataPath, string $size = 'l' ): ?string
    {
        $path = sprintf('%s/profile_banners/%s/%d/%d.jpg',
            rtrim($xfDataPath, '/'), $size, (int) floor($userId / 1000), $userId
        );
        return is_file($path) ? $path : null;
    }

    private function buildUserInfos( MigrationUserPlan $plan, array $roleMap, array $groupMap, string $wpUploadsPath, string $xfDataPath ): array
    {
        $infos = [
            'username' => $plan->xf_username ?: $plan->wp_username,
            'email' => $plan->email,
            'user_group_id' => null,
            'password' => Str::uuid(), // Used for API verifications.
            'xf_user_id' => 0,
            'avatar_path' => null,
            'banner_path' => null,
            'register_date' => null,
            'profile' => [
                'about' => null,
                'website' => null,
            ],
            'source_real_password' => null,
            'wp_password' => null,
            'xf_scheme_class' => null,
            'xf_password_data' => null,
        ];

        if( $plan->wp_user_id )
        {
            $wp = DB::connection('old_wp')
                ->table('users')
                ->leftJoin('usermeta as m1', fn( $j ) => $j->on('users.ID', '=', 'm1.user_id')->where('m1.meta_key', '=', 'description') )
                ->leftJoin('usermeta as m2', fn( $j ) => $j->on('users.ID', '=', 'm2.user_id')->where('m2.meta_key', '=', 'wp_capabilities') )
                ->leftJoin('usermeta as m3', fn( $j ) => $j->on('users.ID', '=', 'm3.user_id')->where('m3.meta_key', '=', 'simple_local_avatar') )
                ->where('users.ID', '=', $plan->wp_user_id )
                ->select('m1.meta_value as description', 'm2.meta_value as capabilities', 'm3.meta_value as avatar_meta', 'users.user_url as website', 'users.user_pass as password', 'users.user_registered' )
                ->first();

            $infos['register_date'] = $wp->user_registered ? strtotime($wp->user_registered) : null;
            $infos['profile']['about'] = $wp->description;
            $infos['profile']['website'] = $wp->website;
            $role = $this->extractWpRole($wp->capabilities);
            $infos['user_group_id'] = $roleMap[$role] ?? $roleMap['contributor'];

            if( $url = $this->extractWpAvatarPath($wp->avatar_meta, $wpUploadsPath)){
                $infos['avatar_path'] = $url;
            }

            $infos['source_real_password'] = 'wp';
            $infos['wp_password'] = $wp->password;
        }

        if( $plan->xf_user_id )
        {
            $xf = DB::connection('old_xf')
                ->table('user')
                 ->leftJoin('user_profile', 'user.user_id', '=', 'user_profile.user_id')
                ->leftJoin('user_authenticate', 'user.user_id', '=', 'user_authenticate.user_id')
                ->where('user.user_id', '=', $plan->xf_user_id)
                ->select('user.avatar_date', 'user.user_group_id', 'user.register_date', 'user_profile.about', 'user_profile.website', 'user_profile.banner_date', 'user_authenticate.scheme_class', 'user_authenticate.data')
                ->first();

            if( !$infos['register_date'] && $xf )
                $infos['register_date'] = $xf->register_date ?: null;
            if( !$infos['profile']['about'] && $xf )
                $infos['profile']['about'] = $xf->about ?: null;
            if( !$infos['profile']['website'] && $xf )
                $infos['profile']['website'] = $xf->website ?: null;

            if( !$plan->wp_user_id ){
                $infos['user_group_id'] = $groupMap[$xf->user_group_id] ?? reset($groupMap);
            }
            $infos['xf_user_id'] = $plan->xf_user_id;

            if( $infos['avatar_path'] === null && (int) $xf->avatar_date > 0){
                if( $path = $this->extractXfAvatarPath($plan->xf_user_id, $xfDataPath)){
                    $infos['avatar_path'] = $path;
                }
            }

            if( $infos['banner_path'] === null && (int) $xf->banner_date > 0 ){
                if( $path = $this->extractXfBannerPath($plan->xf_user_id, $xfDataPath)){
                    $infos['banner_path'] = $path;
                }
            }

            if( $infos['source_real_password'] === null && $xf->scheme_class && $xf->data ){
                $infos['source_real_password'] = 'xf';
                $infos['xf_scheme_class'] = $xf->scheme_class;
                $infos['xf_password_data'] = $xf->data;
            }
        }

        return $infos;
    }

    private function logMap( string $sourceSystem, string $sourceTable, int $sourceId, int $targetId )
    {
        DB::table('migrations_logs')->insert([
            'source_system' => $sourceSystem,
            'source_table' => $sourceTable,
            'source_id' => $sourceId,
            'target_table' => 'xf_user',
            'target_id' => $targetId,
            'status' => 'done',
            'migrated_at' => now(),
            'created_at' => now(),
            'updated_at' => now()
        ]);
    }

    public function handle()
    {
        $wpUploadsPath = $this->option('wp-uploads-path');
        $xfDataPath = $this->option('xf-data-path');

        if( !$wpUploadsPath || !is_dir($wpUploadsPath) ){
            $this->error('Missing WP Uploads Path');
            return self::FAILURE;
        }

        if( !$xfDataPath || !is_dir($xfDataPath) ){
            $this->error('Missing XF Data Path');
            return self::FAILURE;
        }

        $roleMap = json_decode(DB::table('migration_settings')->where('key', 'wp_role_to_xf_group')->value('value'), true);
        $groupMap = json_decode(DB::table('migration_settings')->where('key', 'old_xf_group_to_xf_group')->value('value'), true);

        if( !$roleMap || !$groupMap ) {
            $this->error('Role map and group map are required.');
            return self::FAILURE;
        }

        $query = MigrationUserPlan::where('status', 'approved')->whereNull('user_id');
        if( $limit = $this->option('limit') ) {
            $query->limit((int) $limit);
        }

        $rows = $query->get();
        $this->info("{$rows->count()} accounts will be created on XenForo database !!!.");
        $ok = $this->ask("Write 'ok' if you want to start the migration. Everything else to quit the migration.");
        if( $ok !== 'ok' ) {
            return self::SUCCESS;
        }

        $service = app(XenforoApiService::class);

        $this->withProgressBar($rows, function($plan) use( $roleMap, $groupMap, $wpUploadsPath, $xfDataPath, $service ) {
           try {
                $infos = $this->buildUserInfos( $plan, $roleMap, $groupMap, $wpUploadsPath, $xfDataPath );

                [ $userId, $passwordSet ] = $service->_migrateUser( $infos );
                if( !$userId ){
                    throw new \RuntimeException("Error when user creation.");
                }

                MigrationUserPlan::where('id', $plan->id )->update([ 'user_id' => $userId ]);

                $this->logMap( $plan->wp_user_id ? 'wp' : 'xf', $plan->wp_user_id ? 'wp_users' : 'xf_user', $plan->wp_user_id ?? $plan->xf_user_id, $userId );
                if( $plan->wp_user_id && $plan->xf_user_id ){
                    $this->logMap('xf', 'xf_user', $plan->xf_user_id, $userId );
                }


           } catch ( \Throwable $e ) {
               Log::error("Unable to create Plan#{$plan->id} user : {$e->getMessage()}");
           }
        });

        $this->newLine(2);
        $this->info("Process finished.");
        return self::SUCCESS;
    }
}
