<?php

namespace App\Http\Controllers;

use App\Models\Revision;
use App\Models\Row;
use App\Models\Table;
use App\Models\User;
use App\Utils\Column;
use Illuminate\Http\Request;
use MightyPork\Exceptions\NotApplicableException;

class TableController extends Controller
{
    public function view(User $user, string $table)
    {
        /** @var Table $tableModel */
        $tableModel = $user->tables()->where('name', $table)->first();
        $revision = $tableModel->activeRevision;

        return view('table.view', [
            'table' => $tableModel,
            'revision' => $revision,
            'columns' => Column::columnsFromJson(json_decode($revision->columns)),
            'rows' => $revision->rows,
        ]);
    }

    /**
     * SHow a form for creating a new table
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        $exampleColumns =
            "latin,string,Latin Name\n" .
            "common,string,Common Name\n" .
            "lifespan,int,Lifespan (years)";

        $exampleData =
            "Mercenaria mercenaria,hard clam,40\n" .
            "Magallana gigas,pacific oyster,30\n" .
            "Patella vulgata,common limpet,20";

        return view('table.create',
            compact('exampleColumns', 'exampleData')
        );
    }

    public function storeNew(Request $request)
    {
        /** @var User $u */
        $u = \Auth::user();

        $this->validate($request, [
            'name' => 'required',
            'title' => 'string',
            'description' => 'string|nullable',
            'license' => 'string|nullable',
            'origin' => 'string|nullable',
            'columns' => 'required',
            'data' => 'string|nullable',
        ]);

        // Check if table name is unique for user
        $tabName = $request->get('name');
        if ($u->tables()->where('name', $tabName)->exists()) {
            return $this->backWithErrors([
                'name' => "A table called \"$tabName\" already exists in your account.",
            ]);
        }

        // Parse and validate the columns specification
        /** @var Column[] $columns */
        $columns = [];
        $colTable = array_map('str_getcsv', explode("\n", $request->get('columns')));
        foreach ($colTable as $col) {
            $col = array_map('trim', $col);
            if (count($col) < 2) {
                return $this->backWithErrors([
                    'columns' => "All columns must have at least name and type.",
                ]);
            }

            try {
                $columns[] = new Column([
                    'name' => $col[0],
                    'type' => $col[1],
                    'title' => count($col) >= 3 ? $col[2] : $col[0], // title falls back to =name if not specified,
                ]);
            } catch (\Exception $e) {
                return $this->backWithErrors(['columns' => $e->getMessage()]);
            }
        }

        $rowTable = array_map('str_getcsv', explode("\n", $request->get('data')));

        // Preparing data to insert into the Rows table
        $rowsData = null;
        try {
            $rowsData = array_map(function ($row) use ($columns) {
                if (count($row) != count($columns)) {
                    throw new NotApplicableException("All rows must have " . count($columns) . " fields.");
                }
                $parsed = [];
                foreach ($row as $i => $val) {
                    $key = $columns[$i]->name;
                    $parsed[$key] = $columns[$i]->cast($val);
                }
                return [
                    'data' => json_encode($parsed),
                    'refs' => 1,
                ];
            }, $rowTable);
        } catch (\Exception $e) {
            return $this->backWithErrors(['columns' => $e->getMessage()]);
        }

        $revision = Revision::create([
            'refs' => 1, // from the new table
            'note' => "Initial revision of table $u->name/$tabName",
            'columns' => json_encode($columns),
        ]);

        $table = Table::create([
            'owner_id' => $u->id,
            'revision_id' => $revision->id,
            'name' => $tabName,
            'title' => $request->get('title'),
            'description' => $request->get('description'),
            'license' => $request->get('license'),
            'origin' => $request->get('origin'),
        ]);

        // Attach the revision to the table, set as current
        $table->revisions()->attach($revision);
        $table->activeRevision()->associate($revision);

        // Spawn the rows, linked to the revision
        $revision->rows()->createMany($rowsData);

        // Now we create rows, a revision pointing to them, and the table using it.

        return "Ok.";
    }
}