<?php

declare(strict_types=1);

namespace App\Services;

use App\Contracts\ImportExport;
use App\Contracts\Service;
use App\Models\Aircraft;
use App\Models\Airport;
use App\Models\Expense;
use App\Models\Fare;
use App\Models\Flight;
use App\Models\FlightFieldValue;
use App\Models\Subfleet;
use App\Services\ImportExport\AircraftImporter;
use App\Services\ImportExport\AirportImporter;
use App\Services\ImportExport\ExpenseImporter;
use App\Services\ImportExport\FareImporter;
use App\Services\ImportExport\FlightImporter;
use App\Services\ImportExport\SubfleetImporter;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use League\Csv\Exception;
use League\Csv\Reader;

class ImportService extends Service
{
    /**
     * Throw a validation error back up because it will automatically show
     * itself under the CSV file upload, and nothing special needs to be done
     *
     *
     * @throws ValidationException
     */
    protected function throwError($error, ?\Exception $e = null): void
    {
        Log::error($error);
        if ($e instanceof \Exception) {
            Log::error($e->getMessage());
        }

        $validator = Validator::make([], []);
        $validator->errors()->add('csv_file', $error);

        throw new ValidationException($validator);
    }

    /**
     * @throws ValidationException
     */
    public function openCsv(string $csv_file): ?Reader
    {
        try {
            $reader = Reader::createFromPath($csv_file, 'r');
            $reader->setDelimiter(',');
            $reader->setHeaderOffset(0);
            $reader->setEnclosure('"');
            $reader->setEscape('\\');

            return $reader;
        } catch (Exception $exception) {
            $this->throwError('Error opening CSV: '.$exception->getMessage(), $exception);
        }

        return null;
    }

    /**
     * Run the actual importer, pass in one of the Import classes which implements
     * the ImportExport interface
     *
     *
     * @throws ValidationException
     */
    protected function runImport(string $file_path, ImportExport $importer): array
    {
        $reader = $this->openCsv($file_path);

        array_keys($importer->getColumns());
        $header_rows = $reader->getHeader();
        $records = $reader->getRecords($header_rows);
        foreach ($records as $offset => $row) {
            // turn it into a collection and run some filtering
            $row = collect($row)->map(function ($val, $index): string {
                $val = trim($val);

                return str_ireplace(['\\n', '\\r'], '', $val);
            })->toArray();

            // Try to validate
            $validator = Validator::make($row, $importer->getColumns());
            if ($validator->fails()) {
                $errors = 'Error in row '.$offset.','.implode(';', $validator->errors()->all());
                $importer->errorLog($errors);

                continue;
            }

            $importer->import($row, $offset);
        }

        return $importer->status;
    }

    /**
     * Import aircraft
     *
     *
     * @throws ValidationException
     */
    public function importAircraft(string $csv_file, bool $delete_previous = true): array
    {
        if ($delete_previous) {
            Aircraft::truncate();

            // Log::warning('Aircraft table truncated by User: '.Auth::id().' , '.Auth::user()->name_private);
        }

        $importer = new AircraftImporter();

        return $this->runImport($csv_file, $importer);
    }

    /**
     * Import airports
     *
     *
     * @throws ValidationException
     */
    public function importAirports(string $csv_file, bool $delete_previous = true): array
    {
        if ($delete_previous) {
            Airport::truncate();
        }

        $importer = new AirportImporter();

        return $this->runImport($csv_file, $importer);
    }

    /**
     * Import expenses
     *
     *
     * @throws ValidationException
     */
    public function importExpenses(string $csv_file, bool $delete_previous = true): array
    {
        if ($delete_previous) {
            Expense::truncate();
        }

        $importer = new ExpenseImporter();

        return $this->runImport($csv_file, $importer);
    }

    /**
     * Import fares
     *
     *
     * @throws ValidationException
     */
    public function importFares(string $csv_file, bool $delete_previous = true): array
    {
        if ($delete_previous) {
            Fare::truncate();
        }

        $importer = new FareImporter();

        return $this->runImport($csv_file, $importer);
    }

    /**
     * Import flights
     *
     *
     * @throws ValidationException
     */
    public function importFlights(string $csv_file, ?string $delete_previous = null): array
    {
        if (!in_array($delete_previous, [null, '', '0'], true)) {
            // If delete_previous contains all, then delete everything
            if ($delete_previous === 'all') {
                Flight::truncate();
                FlightFieldValue::truncate();
            } elseif ($delete_previous === 'core') {
                // Delete all flights where the owner_type is null
                Flight::whereNull('owner_type')->delete();
            }
        }

        $importer = new FlightImporter();

        return $this->runImport($csv_file, $importer);
    }

    /**
     * Import subfleets
     *
     *
     * @throws ValidationException
     */
    public function importSubfleets(string $csv_file, bool $delete_previous = true): array
    {
        if ($delete_previous) {
            Subfleet::truncate();
            DB::table('subfleet_rank')->truncate();
            DB::table('flight_subfleet')->truncate();
            DB::table('typerating_subfleet')->truncate();

            // Log::warning('Subfleet and tied relationship tables truncated by User: '.Auth::id().' , '.Auth::user()->name_private);
        }

        $importer = new SubfleetImporter();

        return $this->runImport($csv_file, $importer);
    }
}
