<?php

namespace Snaju\Inception\Http\Request\Validators\Rules;

use Snaju\Inception\Exception\InvalidArgumentException;
use Snaju\Inception\Http\Request\Validators\ValidatorRule;
use Snaju\Inception\ORM\DataModel;
use Snaju\Inception\ORM\ORM;

/**
 * Validates that the value provided for the specified field is unique and doesn't have any same values in the specified database.
 * The database table name or FQPN may be used.
 *
 * @package Snaju\Inception\Http\Request\Validators\Rules
 */
class UniqueRule extends ValidatorRule
{

    public function isValid($dataArray): bool
    {
        $valid = $this->optional;
        $bodyData = $dataArray['body'];

        if (array_key_exists($this->field, $bodyData)) {
            $count = 0;
            $parts = explode(',', $this->ruleValue);
            $tableName = $parts[0];

            // If namespace is provided, get the table name from the class
            if (str_contains($this->ruleValue, '\\')) {
                $namespace = $parts[0];
                $class = new $namespace;

                if (! $class instanceof DataModel) {
                    throw new InvalidArgumentException("$namespace is not a valid namespace. Model must extend Snaju\Inception\ORM\DataModal");
                }

                $tableName = ORM::getEntityManager()->getClassMetadata($namespace)->getTableName();
            }

            // Get list of table names and ensure a correct table name is used
            $sm = ORM::getEntityManager()->getConnection()->getSchemaManager();
            $tables = array_map(function($table) {
                return $table->getname();
            }, $sm->listTables());

            if (! in_array($tableName, $tables)) {
                throw new InvalidArgumentException("`$tableName` is not a valid table name. Check model to ensure correct table name is used.");
            }

            // Create and run the query to check the count
            $columnName = isset($parts[1]) ? $parts[1] : $this->field;
            $q = "SELECT COUNT(*) FROM $tableName WHERE $columnName = '{$bodyData[$this->field]}'";
            $stmt = ORM::getEntityManager()->getConnection()->prepare($q);
            $count = (int)$stmt->executeQuery()->fetchNumeric()[0];

            if ($count <= 0) {
                $valid = true;
            }
                else if (array_key_exists('id', $bodyData)) {
                    // Grab all the entities that match the query and check
                    // if model id matches the id in the request body allow rule to pass
                    $className = DataModel::getClassNameFromTableName($tableName);

                    if (! empty($className)) {
                        $class = new $className;
                        $model = $class::get($bodyData['id']);

                        if ($bodyData[$columnName] === $model->{$columnName}) {
                            $valid = true;
                        }
                    }
                }
                else {
                    $valid = false;
                }
        }

        return $valid;
    }

    public function getErrorMessage(): string
    {
        return "`{$this->field}` must be unique. Another record has the same value for {$this->field}.";
    }

    public function stopOnError(): bool
    {
        return false;
    }

}