<?php

namespace App\Http\Controllers\Admin;

use DB, Cache;
use App\{
	AccountInterstitial,
	DiscoverCategory,
	DiscoverCategoryHashtag,
	Hashtag,
	Media,
	Profile,
	Status,
	StatusHashtag,
	User
};
use App\Models\ConfigCache;
use App\Models\AutospamCustomTokens;
use App\Services\AccountService;
use App\Services\ConfigCacheService;
use App\Services\StatusService;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use League\ISO3166\ISO3166;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Http;
use App\Http\Controllers\PixelfedDirectoryController;
use \DateInterval;
use \DatePeriod;
use App\Http\Resources\AdminSpamReport;
use App\Util\Lexer\Classifier;
use App\Jobs\AutospamPipeline\AutospamPretrainPipeline;
use App\Jobs\AutospamPipeline\AutospamPretrainNonSpamPipeline;
use App\Jobs\AutospamPipeline\AutospamUpdateCachedDataPipeline;
use Illuminate\Support\Facades\URL;
use App\Services\AutospamService;

trait AdminAutospamController
{
	public function autospamHome(Request $request)
	{
		return view('admin.autospam.home');
	}

	public function getAutospamConfigApi(Request $request)
	{
		$open = Cache::remember('admin-dash:reports:spam-count', 3600, function() {
			return AccountInterstitial::whereType('post.autospam')->whereNull('appeal_handled_at')->count();
		});

		$closed = Cache::remember('admin-dash:reports:spam-count-closed', 3600, function() {
			return AccountInterstitial::whereType('post.autospam')->whereNotNull('appeal_handled_at')->count();
		});

		$thisWeek = Cache::remember('admin-dash:reports:spam-count-stats-this-week ', 86400, function() {
			$sr = config('database.default') == 'pgsql' ? "to_char(created_at, 'MM-YYYY')" : "DATE_FORMAT(created_at, '%m-%Y')";
			$gb = config('database.default') == 'pgsql' ? [DB::raw($sr)] : DB::raw($sr);
			$s = AccountInterstitial::select(
					DB::raw('count(id) as count'),
					DB::raw($sr . " as month_year")
				)
				->where('created_at', '>=', now()->subWeeks(52))
				->groupBy($gb)
				->get()
				->map(function($s) {
					$dt = now()->parse('01-' . $s->month_year);
					return [
						'id' => $dt->format('Ym'),
						'x' => $dt->format('M Y'),
						'y' => $s->count
					];
				})
				->sortBy('id')
				->values()
				->toArray();
			return $s;
		});

		$files = [
			'spam' => [
				'exists' => Storage::exists(AutospamService::MODEL_SPAM_PATH),
				'size' => 0
			],
			'ham' => [
				'exists' => Storage::exists(AutospamService::MODEL_HAM_PATH),
				'size' => 0
			],
			'combined' => [
				'exists' => Storage::exists(AutospamService::MODEL_FILE_PATH),
				'size' => 0
			]
		];

		if($files['spam']['exists']) {
			$files['spam']['size'] = Storage::size(AutospamService::MODEL_SPAM_PATH);
		}

		if($files['ham']['exists']) {
			$files['ham']['size'] = Storage::size(AutospamService::MODEL_HAM_PATH);
		}

		if($files['combined']['exists']) {
			$files['combined']['size'] = Storage::size(AutospamService::MODEL_FILE_PATH);
		}

		return [
			'autospam_enabled' => (bool) config_cache('pixelfed.bouncer.enabled') ?? false,
			'nlp_enabled' => (bool) AutospamService::active(),
			'files' => $files,
			'open' => $open,
			'closed' => $closed,
			'graph' => collect($thisWeek)->map(fn($s) => $s['y'])->values(),
			'graphLabels' => collect($thisWeek)->map(fn($s) => $s['x'])->values()
		];
	}

    public function getAutospamReportsClosedApi(Request $request)
    {
		$appeals = AdminSpamReport::collection(
			AccountInterstitial::orderBy('id', 'desc')
			->whereType('post.autospam')
			->whereIsSpam(true)
			->whereNotNull('appeal_handled_at')
			->cursorPaginate(6)
			->withQueryString()
		);

		return $appeals;
    }

    public function postAutospamTrainSpamApi(Request $request)
    {
    	$aiCount = AccountInterstitial::whereItemType('App\Status')
        	->whereIsSpam(true)
        	->count();
        abort_if($aiCount < 100, 422, 'You don\'t have enough data to pre-train against.');

        $existing = Cache::get('pf:admin:autospam:pretrain:recent');
        abort_if($existing, 422, 'You\'ve already run this recently, please wait 30 minutes before pre-training again');
        AutospamPretrainPipeline::dispatch();
        Cache::put('pf:admin:autospam:pretrain:recent', 1, 1440);

        return [
        	'msg' => 'Success!'
        ];
    }

    public function postAutospamTrainNonSpamSearchApi(Request $request)
    {
    	$this->validate($request, [
    		'q' => 'required|string|min:1'
    	]);

    	$q = $request->input('q');

    	$res = Profile::whereNull(['status', 'domain'])
    		->where('username', 'like', '%' . $q . '%')
    		->orderByDesc('followers_count')
    		->take(10)
    		->get()
    		->map(function($p) {
    			$acct = AccountService::get($p->id, true);
    			return [
    				'id' => (string) $p->id,
    				'avatar' => $acct['avatar'],
    				'username' => $p->username
    			];
    		})
    		->values();
    	return $res;
    }

    public function postAutospamTrainNonSpamSubmitApi(Request $request)
    {
    	$this->validate($request, [
    		'accounts' => 'required|array|min:1|max:10'
    	]);

    	$accts = $request->input('accounts');

    	$accounts = Profile::whereNull(['domain', 'status'])->find(collect($accts)->map(function($a) { return $a['id'];}));

    	abort_if(!$accounts || !$accounts->count(), 422, 'One or more of the selected accounts are not valid');

    	AutospamPretrainNonSpamPipeline::dispatch($accounts);
    	return $accounts;
    }

    public function getAutospamCustomTokensApi(Request $request)
    {
		return AutospamCustomTokens::latest()->cursorPaginate(6);
    }

    public function saveNewAutospamCustomTokensApi(Request $request)
    {
    	$this->validate($request, [
    		'token' => 'required|unique:autospam_custom_tokens,token',
    	]);

    	$ct = new AutospamCustomTokens;
    	$ct->token = $request->input('token');
    	$ct->weight = $request->input('weight');
    	$ct->category = $request->input('category') === 'spam' ? 'spam' : 'ham';
    	$ct->note = $request->input('note');
    	$ct->active = $request->input('active');
    	$ct->save();

    	AutospamUpdateCachedDataPipeline::dispatch();
    	return $ct;
    }

    public function updateAutospamCustomTokensApi(Request $request)
    {
    	$this->validate($request, [
    		'id' => 'required',
    		'token' => 'required',
    		'category' => 'required|in:spam,ham',
    		'active' => 'required|boolean'
    	]);

    	$ct = AutospamCustomTokens::findOrFail($request->input('id'));
    	$ct->weight = $request->input('weight');
    	$ct->category = $request->input('category');
    	$ct->note = $request->input('note');
    	$ct->active = $request->input('active');
    	$ct->save();

    	AutospamUpdateCachedDataPipeline::dispatch();

    	return $ct;
    }

    public function exportAutospamCustomTokensApi(Request $request)
    {
    	abort_if(!Storage::exists(AutospamService::MODEL_SPAM_PATH), 422, 'Autospam Dataset does not exist, please train spam before attempting to export');
    	return Storage::download(AutospamService::MODEL_SPAM_PATH);
    }

    public function enableAutospamApi(Request $request)
    {
    	ConfigCacheService::put('autospam.nlp.enabled', true);
    	Cache::forget(AutospamService::CHCKD_CACHE_KEY);
    	return ['msg' => 'Success'];
    }

    public function disableAutospamApi(Request $request)
    {
    	ConfigCacheService::put('autospam.nlp.enabled', false);
    	Cache::forget(AutospamService::CHCKD_CACHE_KEY);
    	return ['msg' => 'Success'];
    }
}
