<?php

namespace PortalPeople\indicator\controllers;

use Yii;
use yii\web\Controller;
use yii\filters\AccessControl;
use yii\web\ForbiddenHttpException;
use yii\data\ArrayDataProvider;
use PortalPeople\indicator\models\ConfigForm;
use PortalPeople\indicator\services\FtpDownloadService;
use PortalPeople\indicator\Module;

class ConfigController extends Controller
{
    

    public function actionIndex()
    {
        $model = new ConfigForm();
        $model->loadFromParams();

        if ($model->load(Yii::$app->request->post()) && $model->saveToParams()) {
            Yii::$app->session->setFlash('success', 'Indicator configuration saved successfully.');

            return $this->redirect(['index']);
        }

        return $this->render('index', [
            'model' => $model,
            'testResult' => null,
        ]);
    }

    public function actionTestConnection()
    {
        if (!Yii::$app->request->isAjax) {
            throw new ForbiddenHttpException('Invalid request');
        }

        $model = new ConfigForm();

        if ($model->load(Yii::$app->request->post())) {
            if (!$model->validate()) {
                return $this->asJson([
                    'success' => false,
                    'errors' => $model->getErrors(),
                ]);
            }

            $service = new FtpDownloadService([
                'protocol' => $model->protocol,
                'host' => $model->ftpHost,
                'port' => $model->ftpPort,
                'username' => $model->ftpUser,
                'password' => $model->ftpPassword,
                'remotePath' => $model->ftpPath,
                'deleteAfterDownload' => false,
                'inboxPath' => Yii::getAlias('@runtime/indicator/test'),
                'passiveMode' => $model->passiveMode,
            ]);

            $result = $service->download();

            return $this->asJson([
                'success' => empty($result['errors']),
                'message' => empty($result['errors']) 
                    ? 'Connection successful!' 
                    : 'Connection failed: ' . implode(', ', $result['errors']),
                'errors' => $result['errors'],
            ]);
        }

        return $this->asJson(['success' => false, 'message' => 'Invalid data']);
    }

    public function actionDryRun()
    {
        if (!Yii::$app->request->isAjax) {
            throw new ForbiddenHttpException('Invalid request');
        }

        /** @var Module $module */
        $module = Yii::$app->getModule('indicator');

        if (!$module || !$module->isConfigured()) {
            return $this->asJson([
                'success' => false,
                'message' => 'Indicator module not configured',
            ]);
        }

        $dryDir = Yii::getAlias('@runtime/indicator/dry-run');
        @mkdir($dryDir, 0755, true);

        $service = new FtpDownloadService([
            'protocol' => $module->protocol,
            'host' => $module->host,
            'port' => $module->port,
            'username' => $module->username,
            'password' => $module->password,
            'remotePath' => $module->remotePath,
            'deleteAfterDownload' => false,
            'inboxPath' => $dryDir,
            'passiveMode' => $module->passiveMode,
        ]);

        $result = $service->download();

        // Clean up dry-run files
        array_map('unlink', glob($dryDir . '/*.xml'));
        @rmdir($dryDir);

        return $this->asJson([
            'success' => empty($result['errors']),
            'downloaded' => count($result['downloaded']),
            'errors' => $result['errors'],
        ]);
    }

    public function actionStatus()
    {
        if (!Yii::$app->request->isAjax) {
            throw new ForbiddenHttpException('Invalid request');
        }

        /** @var Module $module */
        $module = Yii::$app->getModule('indicator');

        if (!$module || !$module->isConfigured()) {
            return $this->asJson([
                'success' => false,
                'message' => 'Indicator module not configured',
            ]);
        }

        $inboxDir = Yii::getAlias('@runtime/indicator/inbox');
        $processedDir = Yii::getAlias('@runtime/indicator/processed');
        $failedDir = Yii::getAlias('@runtime/indicator/failed');

        $inboxCount = is_dir($inboxDir) ? count(glob($inboxDir . '/*.xml')) : 0;
        $processedCount = is_dir($processedDir) ? count(glob($processedDir . '/*.xml')) : 0;
        $failedCount = is_dir($failedDir) ? count(glob($failedDir . '/*.xml')) : 0;

        return $this->asJson([
            'success' => true,
            'inbox' => $inboxCount,
            'processed' => $processedCount,
            'failed' => $failedCount,
        ]);
    }

    // -------------------------------------------------------------------------
    // FTP file manager helpers
    // -------------------------------------------------------------------------

    private function openFtpConnection(Module $module)
    {
        $proto = strtolower($module->protocol);
        $conn = ($proto === 'ftps')
            ? @ftp_ssl_connect($module->host, $module->port, 10)
            : @ftp_connect($module->host, $module->port, 10);

        if (!$conn) {
            return null;
        }

        if (!@ftp_login($conn, $module->username, $module->password)) {
            ftp_close($conn);
            return null;
        }

        if ($module->passiveMode) {
            ftp_pasv($conn, true);
        }

        return $conn;
    }

    public function actionListFiles()
    {
        if (!Yii::$app->request->isAjax) {
            throw new ForbiddenHttpException('Invalid request');
        }

        /** @var Module $module */
        $module = Yii::$app->getModule('indicator');

        if (!$module || !$module->isConfigured()) {
            return $this->asJson(['success' => false, 'message' => 'Module not configured']);
        }

        $conn = $this->openFtpConnection($module);
        if (!$conn) {
            return $this->asJson(['success' => false, 'message' => 'Could not connect']);
        }

        $mainPath      = rtrim($module->remotePath ?: '/', '/') ?: '/';
        $processedPath = rtrim($module->moveToPath ?: '/processed', '/') ?: '/processed';

        $parseDate = static function (string $m, string $d, string $ty): int {
            if (strpos($ty, ':') !== false) {
                $ts = strtotime("$m $d " . date('Y') . " $ty");
                if ($ts !== false && $ts > time()) {
                    $ts = strtotime("$m $d " . (date('Y') - 1) . " $ty");
                }
            } else {
                $ts = strtotime("$m $d $ty");
            }
            return $ts !== false ? (int) $ts : 0;
        };

        $listDir = static function ($path) use ($conn, $parseDate) {
            $raw = @ftp_rawlist($conn, $path);
            if (!is_array($raw)) {
                return [];
            }
            $files = [];
            foreach ($raw as $line) {
                // skip directories (lines starting with 'd')
                if (empty($line) || $line[0] === 'd') {
                    continue;
                }
                // parse: perms links owner group size month day time name
                $parts = preg_split('/\s+/', $line, 9);
                if (count($parts) < 9) {
                    continue;
                }
                $name = $parts[8];
                if (!preg_match('/\.xml$/i', $name)) {
                    continue;
                }
                $files[] = [
                    'name'      => $name,
                    'size'      => (int) $parts[4],
                    'modified'  => trim("{$parts[5]} {$parts[6]} {$parts[7]}"),
                    'timestamp' => $parseDate($parts[5], $parts[6], $parts[7]),
                ];
            }
            return $files;
        };

        $mainFiles      = $listDir($mainPath);
        $processedFiles = $listDir($processedPath);

        // Newest first
        usort($processedFiles, static function ($a, $b) { return $b['timestamp'] - $a['timestamp']; });

        ftp_close($conn);

        return $this->asJson([
            'success'         => true,
            'mainPath'        => $mainPath,
            'processedPath'   => $processedPath,
            'mainFiles'       => $mainFiles,
            'processedFiles'  => $processedFiles,
        ]);
    }

    public function actionFiles()
    {
        /** @var Module $module */
        $module = Yii::$app->getModule('indicator');

        if (!$module || !$module->isConfigured()) {
            Yii::$app->session->setFlash('error', 'Indicator module not configured — please save configuration first.');
            return $this->redirect(['index']);
        }

        $mainPath      = rtrim($module->remotePath ?: '/', '/') ?: '/';
        $processedPath = rtrim($module->moveToPath ?: '/processed', '/') ?: '/processed';
        $mainFiles     = [];
        $processedFiles = [];
        $error         = null;

        $conn = $this->openFtpConnection($module);
        if (!$conn) {
            $error = 'Could not connect to FTP server — check configuration.';
        } else {
            $parseDate = static function (string $m, string $d, string $ty): int {
                if (strpos($ty, ':') !== false) {
                    $ts = strtotime("$m $d " . date('Y') . " $ty");
                    if ($ts !== false && $ts > time()) {
                        $ts = strtotime("$m $d " . (date('Y') - 1) . " $ty");
                    }
                } else {
                    $ts = strtotime("$m $d $ty");
                }
                return $ts !== false ? (int) $ts : 0;
            };

            $listDir = static function (string $path) use ($conn, $parseDate): array {
                $raw = @ftp_rawlist($conn, $path);
                if (!is_array($raw)) {
                    return [];
                }
                $files = [];
                foreach ($raw as $line) {
                    if (empty($line) || $line[0] === 'd') {
                        continue;
                    }
                    $parts = preg_split('/\s+/', $line, 9);
                    if (count($parts) < 9) {
                        continue;
                    }
                    $name  = $parts[8];
                    if (!preg_match('/\.xml$/i', $name)) {
                        continue;
                    }
                    $bytes = (int) $parts[4];
                    $ts    = $parseDate($parts[5], $parts[6], $parts[7]);
                    $files[] = [
                        'name'      => $name,
                        'sizeBytes' => $bytes,
                        'size'      => $bytes < 1048576
                            ? round($bytes / 1024, 1) . ' KB'
                            : round($bytes / 1048576, 1) . ' MB',
                        'timestamp' => $ts,
                        'modified'  => $ts ? date('d M Y H:i', $ts) : trim("{$parts[5]} {$parts[6]} {$parts[7]}"),
                    ];
                }
                return $files;
            };

            $mainFiles      = $listDir($mainPath);
            $processedFiles = $listDir($processedPath);
            ftp_close($conn);
        }

        $sortCfg = [
            'attributes' => [
                'name',
                'size'     => ['asc' => ['sizeBytes' => SORT_ASC],  'desc' => ['sizeBytes' => SORT_DESC]],
                'modified' => ['asc' => ['timestamp' => SORT_ASC],  'desc' => ['timestamp' => SORT_DESC]],
            ],
        ];

        $mainProvider = new ArrayDataProvider([
            'allModels'  => $mainFiles,
            'key'        => 'name',
            'sort'       => array_merge($sortCfg, ['defaultOrder' => ['name' => SORT_ASC]]),
            'pagination' => ['pageSize' => 20, 'pageParam' => 'mp'],
        ]);

        $processedProvider = new ArrayDataProvider([
            'allModels'  => $processedFiles,
            'key'        => 'name',
            'sort'       => array_merge($sortCfg, ['defaultOrder' => ['modified' => SORT_DESC]]),
            'pagination' => ['pageSize' => 20, 'pageParam' => 'pp'],
        ]);

        return $this->render('files', [
            'mainProvider'      => $mainProvider,
            'processedProvider' => $processedProvider,
            'mainPath'          => $mainPath,
            'processedPath'     => $processedPath,
            'error'             => $error,
        ]);
    }

    public function actionMoveFile()
    {
        $isAjax   = Yii::$app->request->isAjax;
        $filename = basename(Yii::$app->request->post('file', ''));
        $from     = Yii::$app->request->post('from', '');

        $fail = function (string $msg) use ($isAjax) {
            if ($isAjax) {
                return $this->asJson(['success' => false, 'message' => $msg]);
            }
            Yii::$app->session->setFlash('error', $msg);
            return $this->redirect(['files']);
        };

        if (!$filename || !preg_match('/\.xml$/i', $filename)) {
            return $fail('Invalid filename');
        }

        /** @var Module $module */
        $module = Yii::$app->getModule('indicator');
        if (!$module || !$module->isConfigured()) {
            return $fail('Module not configured');
        }

        $mainPath      = rtrim($module->remotePath ?: '/', '/') ?: '/';
        $processedPath = rtrim($module->moveToPath ?: '/processed', '/') ?: '/processed';

        if ($from !== 'processed') {
            return $fail('Invalid from value');
        }

        $srcFile  = $processedPath . '/' . $filename;
        $destFile = $mainPath . '/' . $filename;

        $conn = $this->openFtpConnection($module);
        if (!$conn) {
            return $fail('Could not connect');
        }

        $ok = @ftp_rename($conn, $srcFile, $destFile);
        ftp_close($conn);

        if (!$ok) {
            return $fail("Could not move {$filename} — check paths and permissions");
        }

        if ($isAjax) {
            return $this->asJson(['success' => true, 'message' => "{$filename} moved back to main folder"]);
        }
        Yii::$app->session->setFlash('success', "{$filename} moved back to main folder");
        return $this->redirect(['files']);
    }

    public function actionArchiveProcessed()
    {
        if (!Yii::$app->request->isAjax) {
            throw new ForbiddenHttpException('Invalid request');
        }

        /** @var Module $module */
        $module = Yii::$app->getModule('indicator');
        if (!$module || !$module->isConfigured()) {
            return $this->asJson(['success' => false, 'message' => 'Module not configured']);
        }

        $conn = $this->openFtpConnection($module);
        if (!$conn) {
            return $this->asJson(['success' => false, 'message' => 'Could not connect']);
        }

        $processedPath = rtrim($module->moveToPath ?: '/processed', '/') ?: '/processed';
        $archivePath   = $processedPath . '/archive';

        @ftp_mkdir($conn, $archivePath);

        $parseDate = static function (string $m, string $d, string $ty): int {
            if (strpos($ty, ':') !== false) {
                $ts = strtotime("$m $d " . date('Y') . " $ty");
                if ($ts !== false && $ts > time()) {
                    $ts = strtotime("$m $d " . (date('Y') - 1) . " $ty");
                }
            } else {
                $ts = strtotime("$m $d $ty");
            }
            return $ts !== false ? (int) $ts : 0;
        };

        $raw    = @ftp_rawlist($conn, $processedPath);
        $cutoff = strtotime('-3 months');
        $groups = [];

        if (is_array($raw)) {
            foreach ($raw as $line) {
                if (empty($line) || $line[0] === 'd') {
                    continue;
                }
                $parts = preg_split('/\s+/', $line, 9);
                if (count($parts) < 9) {
                    continue;
                }
                $name = $parts[8];
                if (!preg_match('/\.xml$/i', $name)) {
                    continue;
                }
                $ts = $parseDate($parts[5], $parts[6], $parts[7]);
                if ($ts > 0 && $ts < $cutoff) {
                    $groups[date('Y-m', $ts)][] = $name;
                }
            }
        }

        if (empty($groups)) {
            ftp_close($conn);
            return $this->asJson([
                'success'  => true,
                'archived' => 0,
                'message'  => 'No files older than 3 months to archive',
            ]);
        }

        $tempBase = sys_get_temp_dir() . '/indicator-archive-' . uniqid();
        @mkdir($tempBase, 0755, true);

        $archivedGroups = 0;
        $archivedFiles  = 0;
        $errors         = [];

        foreach ($groups as $monthKey => $filenames) {
            $tempDir = $tempBase . '/' . $monthKey;
            @mkdir($tempDir, 0755, true);
            $downloaded = [];

            foreach ($filenames as $fname) {
                if (@ftp_get($conn, $tempDir . '/' . $fname, $processedPath . '/' . $fname, FTP_BINARY)) {
                    $downloaded[] = $fname;
                } else {
                    $errors[] = "Could not download: {$fname}";
                }
            }

            if (empty($downloaded)) {
                continue;
            }

            $zipPath = $tempBase . '/' . $monthKey . '.zip';
            $zip     = new \ZipArchive();

            if ($zip->open($zipPath, \ZipArchive::CREATE) !== true) {
                $errors[] = "Could not create archive for {$monthKey}";
                continue;
            }

            foreach ($downloaded as $fname) {
                $zip->addFile($tempDir . '/' . $fname, $fname);
            }
            $zip->close();

            if (@ftp_put($conn, $archivePath . '/' . $monthKey . '.zip', $zipPath, FTP_BINARY)) {
                foreach ($downloaded as $fname) {
                    @ftp_delete($conn, $processedPath . '/' . $fname);
                }
                $archivedGroups++;
                $archivedFiles += count($downloaded);
            } else {
                $errors[] = "Could not upload archive {$monthKey}.zip";
            }
        }

        // Clean up temp files
        foreach (glob($tempBase . '/*/*.xml') ?: [] as $f) { @unlink($f); }
        foreach (glob($tempBase . '/*/') ?: [] as $d) { @rmdir($d); }
        foreach (glob($tempBase . '/*.zip') ?: [] as $f) { @unlink($f); }
        @rmdir($tempBase);

        ftp_close($conn);

        $msg = "Archived {$archivedFiles} file(s) into {$archivedGroups} monthly archive(s)";
        if (!empty($errors)) {
            $msg .= ' (with ' . count($errors) . ' error(s))';
        }

        return $this->asJson([
            'success'  => empty($errors),
            'archived' => $archivedFiles,
            'groups'   => $archivedGroups,
            'errors'   => $errors,
            'message'  => $msg,
        ]);
    }
}
