<?php
class AdminGodController extends AdminController {  
  function __construct () {
    parent::__construct();
    $this->load->model($this->model);
    
    $this->model = $this->{$this->model};
    
    $this->data['permissions'] = $this->permissions;
    $this->data['names'] = $this->names;

    $this->data['uploadFolder'] = isset($this->uploadFolder) ? $this->uploadFolder : '';

    $this->data['fields'] = $this->configSelectFields($this->fields);
  }

  public function index () {
    if (array_key_exists('isUnique', $this->permissions) && $this->permissions['isUnique']) {
      redirect('admin/' . $this->names['link'] . '/update');
    }

    if (isset($this->getAllWhere)) {
      $records = $this->model->getAllWhere($this->getAllWhere);
    } else {
      $records = $this->model->getAll();
    }

    $this->data['records'] = $this->configOptionsToTable($records, $this->fields);

    $this->load->view('admin/god-controller/index', $this->data);
  }

  public function create () {
    if ($this->permissions['create']) {
      $this->load->view('admin/god-controller/form', $this->data);
    } else {
      redirect('admin/' . $this->names['link']);
    }
  }

  public function update ($id = null) {
    $isUnique = array_key_exists('isUnique', $this->permissions) && $this->permissions['isUnique'];
    if ($id && $isUnique) {
      redirect('admin/' . $this->names['link'] . '/update');
    }

    if (!$id && $isUnique) {
      $record = $this->model->getLast();
    } else {
      $record = $this->model->getByPrimary($id);
    }
    
    if (!$this->permissions['update'] || !$record) {
      redirect('admin/' . $this->names['link']);
    }

    $this->data['record'] = $record;
    $record->dropzoneImages = $this->getRecordDropzoneImages($record);

    $this->load->view('admin/god-controller/form', $this->data);
  }

  private function getRecordDropzoneImages ($record) {
    $dropzoneFieldKey = array_search('dropzone', array_column($this->fields, 'type'));

    if (!$dropzoneFieldKey) return null;

    $dropzoneField = $this->fields[$dropzoneFieldKey];

    if (!$dropzoneField || !is_array($dropzoneField) || $dropzoneField['type'] != 'dropzone') return null;

    $model = $dropzoneField['model'];
    $this->load->model($model);

    $foreignKey = $dropzoneField['foreignKey'];

    $images = $this->{$model}->getAllWhere([$foreignKey => $record->id]);
    return $images;
  }

  public function delete () {
    $ids = $this->input->post('id');
    foreach ($ids as $id) {
      $record = $this->model->getByPrimary($id);
      
      if (!$this->permissions['delete'] || !$record) {
        return false;
      }

      $this->deleteRecordFiles($id);
      $this->model->delete($id);
    }
  }

  private function deleteRecordFiles ($recordId) {
    $record = $this->model->getByPrimary($recordId);
    if (!$record) {
      return false;
    }
    
    $fields = array_filter($this->fields, function ($field) {
      return $field['type'] == 'image' || $field['type'] == 'video' || $field['type'] == 'file';
    });

    foreach ($fields as $field) {
      $this->deleteFile($recordId, $field['name'], $field['type']);
    }

    return true;
  }
  
  private function deleteFile ($recordId, string $fieldName, string $fileType) {
    $record = $this->model->getByPrimary($recordId);
    
    if (!$record) {
      return false;
    }

    $recordFile = $record->{$fieldName};

    if (!$recordFile) {
      return false;
    }

    $allowedTypes = ['image', 'video', 'file'];
    if (!in_array($fileType, $allowedTypes)) {
      return false;
    }

    $this->load->model('FileModel');
    $this->FileModel->removeFile(
      $this->uploadFolder ? ($this->uploadFolder . '/') : '',
      $recordFile,
      $fileType
    );
  }

  private function addFile (string $fieldName, string $fileType, string $uploadFolder = null) {
    $file = $_FILES[$fieldName];
    $fileName = $file['name'];

    if (!$fileName) return false;

    $allowedTypes = ['image', 'video', 'file'];
    if (!in_array($fileType, $allowedTypes)) {
      return false;
    }

    $this->load->model('FileModel');
    $file = $this->FileModel->uploadFile(
      $uploadFolder ? $uploadFolder : $this->uploadFolder,
      slugify($this->names['plural']) . '-' . $fileName,
      $fieldName,
      $fileType
    );

    if (!$file || !array_key_exists('upload_data', $file)) {
      return false;
    }

    $fileName = $file['upload_data']['file_name'];

    return $fileName;
  }

  private function addFileAndGetName (string $fieldName, string $fileType, string $uploadFolder = null) {
    $this->load->model('FileModel');
    $imageNameOrFalse = $this->addFile($fieldName, $fileType, $uploadFolder);

    if ($imageNameOrFalse) {
      return $imageNameOrFalse;
    }

    return null;
  }

  private function getFieldValue (string $fieldName, array $fieldInfo) {
    if (!array_key_exists('type', $fieldInfo)) return null;
    
    $actions = [
      'date' => function () use ($fieldName) {
        if ($this->input->post($fieldName)) {
          return implode('-', array_reverse(explode('/', $this->input->post($fieldName))));
        }

        return null;
      },
      'user_id' => function () {
        $user = $this->session->userdata('admin');

        if (array_key_exists('id', $user)) {
          return $user['id'];
        }

        return null;
      },
      'password' => function () use ($fieldName) {
        $password = $this->input->post($fieldName);

        if ($password) {
          return encode_crip($password);
        }
      }
    ];

    if (array_key_exists($fieldInfo['type'], $actions)) {
      $action = $actions[$fieldInfo['type']];
      return $action();
    }

    $fileTypes = ['image', 'video', 'file'];
    if (in_array($fieldInfo['type'], $fileTypes)) {
      return $this->addFileAndGetName($fieldName, $fieldInfo['type']);
    }

    return $this->input->post($fieldName);
  }

  public function uploadSummernoteImage () {
    $uploadFolder = $this->uploadFolder . '/summernote';
    $imageName = $this->addFileAndGetName('summernote_image', 'image', $uploadFolder);

    if (!$imageName) {
      echo json_encode(['success' => false]);
      return false;
    }

    echo json_encode(['success' => true, 'imageName' => $imageName]);
  }

  public function save () {
    $this->setRulesValidation($this->data['fields']);

    $recordData = [];

    $dropzoneSettings = [];

    foreach ($this->fields as $field) {
      $hasDisabled = array_key_exists('disabled', $field);
      $isEnabled = !$hasDisabled || ($hasDisabled && !$field['disabled']);
      
      $hasShowOnForm = array_key_exists('showOnForm', $field);
      $showOnForm = !$hasShowOnForm || ($hasShowOnForm && $field['showOnForm']);

      if ($isEnabled && $showOnForm) {
        $fileTypes = ['image', 'video', 'file'];
        
        if (
          array_key_exists('id', $recordData)
          && $recordData['id']
          && in_array($field['type'], $fileTypes)
          && $_FILES[$field['name']]['name']
        ) {
          $this->deleteFile($recordData['id'], $field['name'], $field['type']);
        }

        $ignoredFieldTypes = ['separator', 'slug', 'dropzone'];
        if (!in_array($field['type'], $ignoredFieldTypes)) {
          if (
            !in_array($field['type'], $fileTypes)
            || (array_key_exists($field['name'], $_FILES) && $_FILES[$field['name']]['name'])
          ) {
            $fieldValue = $this->getFieldValue($field['name'], $field);
            if ($field['type'] != 'password' || ($field['type'] == 'password' && $fieldValue)) {
              $recordData[$field['name']] = $fieldValue;
            }
          }
        } else if ($field['type'] == 'dropzone') {
          $dropzoneSettings['foreignKey'] = $field['foreignKey'];
          $dropzoneSettings['model'] = $field['model'];
        }
      }

      if (array_key_exists('slug', $field) && $field['slug'] === true) {
        $recordData['slug'] = slugify($this->input->post($field['name']));
      }
    }

    if ($this->form_validation->run() == false) {
      echo json_encode(['success' => false, 'errors' => strip_tags(validation_errors())]);
      return false;
    }

    $recordId = null;

    if (array_key_exists('id', $recordData) && $recordData['id']) {
      if ($this->permissions['update']) {
        $this->model->update($recordData);
        
        $recordId = $recordData['id'];

        if ($recordId) {
          $this->session->set_flashdata('success', 'Registro editado com sucesso.');
        }
      }
    } else if ($this->permissions['create']) {
      $recordId = $this->model->create($recordData);
      if ($recordId) {
        $this->session->set_flashdata('success', 'Registro criado com sucesso.');
      }
    }

    if (!empty($dropzoneSettings) && $recordId) {
      $this->saveDropzoneImages($dropzoneSettings['model'], $dropzoneSettings['foreignKey'], $recordId);
    }
    
    echo json_encode(['success' => true]);
  }

  public function saveFromTable () {
    $id = $this->input->post('recordId');
    $fieldName = $this->input->post('fieldName');
    $fieldValue = $this->input->post('fieldValue');

    $fieldKey = array_search($fieldName, array_column($this->fields, 'name'));
    $field = $this->fields[$fieldKey];

    if (!$field) {
      echo json_encode(['success' => false, 'errors' => 'Campo não encontrado']);
      return false;
    }

    $recordData = [
      'id' => $id,
      $fieldName => $fieldValue
    ];

    if (array_key_exists('slug', $field)) {
      $slug = slugify($recordData[$fieldName]);
      if ($this->model->count(['slug' => $slug]) > 0) {
        echo json_encode(['success' => false, 'errors' => 'Campo duplicado']);
        return false;
      } else {
        $recordData['slug'] = $slug;
      }
    }

    if ($this->model->update($recordData)) {
      echo json_encode(['success' => true]);
    } else {
      echo json_encode(['success' => false]);
    }
  }

  private function setRulesValidation (array $fields) {
    foreach ($fields as $field) {
      if (array_key_exists('rules', $field)) {
        if ($field['type'] != 'image') {
          $this->form_validation->set_rules($field['name'], $field['label'], $field['rules']);
        } else {
          $id = $this->input->post('id');
          $alreadyHasImage = false;
  
          if ($id) {
            $alreadyHasImage = !!$this->model->getByPrimary($id)->{$field['name']};
            if (empty($_FILES[$field['name']]['name']) && !$alreadyHasImage) {
              $this->form_validation->set_rules($field['name'], $field['label'], $field['rules']);
            }
          } else {
            if (empty($_FILES[$field['name']]['name'])) {
              $this->form_validation->set_rules($field['name'], $field['label'], $field['rules']);
            }
          }
        }
      }
    }
  }

  private function configSelectFields (array $fields)  {
    $fields = array_map(function ($field) {
      if (
        $field['type'] == 'select'
        && array_key_exists('fromDataBase', $field)
        && $field['fromDataBase'] == true
      ) {
        $field['options'] = $this->getSelectFieldOptions($field);
      }

      return $field;
    }, $fields);

    return $fields;
  }

  private function getSelectFieldOptions ($field) {
    $modelName = $field['options']['model'];

    $this->load->model($modelName);
    $model = $this->{$modelName};

    $records = $model->getAll();
    $options = [];

    foreach ($records as $record) {
      $options[$record->{$field['options']['value']}] = $record->{$field['options']['text']};
    }

    return $options;
  }

  private function configOptionsToTable ($records, $fields) {
    foreach ($records as $record) {
      foreach ($fields as $field) {
        if (
          $field['type'] == 'select'
          && array_key_exists('fromDataBase', $field)
          && $field['fromDataBase'] === true
        ) {
          $modelName = $field['options']['model'];
          $this->load->model($modelName);
          $model = $this->{$modelName};

          $row = $model->getByPrimary($record->{$field['name']});
          if (!array_key_exists('editableOnTable', $field) || !$field['editableOnTable']) {
            $record->{$field['name']} = [];
            if ($row) {
              $record->{$field['name']}['selectText'] = $row->{$field['options']['text']};
            } else {
              $record->{$field['name']}['selectText'] = null;
            }
          }
        }
      }
    }

    return $records;
  }

  public function uploadDropzoneImages () {
    $this->addFileAndGetName('dropzone_file_input', 'image', $this->uploadFolder);
  }

  public function deleteDropzoneImages () {
    $dropzoneFieldKey = array_search('dropzone', array_column($this->fields, 'type'));
    
    if (!$dropzoneFieldKey) return null;

    $dropzoneField = $this->fields[$dropzoneFieldKey];
    
    if (!$dropzoneField || !is_array($dropzoneField)) return null;
    
    $model = $dropzoneField['model'];
    $this->load->model($model);
    
    $images = json_decode($this->input->post('images'));
    
    if (!$images) return null;
    
    $this->load->model('FileModel');

    try {
      foreach ($images as $image) {
        if (isset($image->id) && $image->id) {
          $this->load->model('FileModel');
          $this->FileModel->removeFile(
            $this->uploadFolder ? ($this->uploadFolder . '/') : '',
            $image->name,
            'image'
          );
          
          $this->{$model}->delete($image->id);
        }
      }
    } catch (\Throwable $th) {
      throw $th;
    }
  }

  private function saveDropzoneImages (string $model, string $dropzoneForeignKey, $recordId) {
    $images = json_decode($this->input->post('dropzoneImages'));

    if (!$images || !is_array($images)) return null;
    
    $this->load->model($model);
    
    foreach ($images as $key => $file) {
      $fileName = slugify($this->names['plural']) . '-' . $file->name;
      $fileName = setFileName($fileName);

      if (!isset($file->id)) {
        $this->{$model}->create([
          'image' => $fileName,
          $dropzoneForeignKey => $recordId,
          'size' => $file->size,
          'sort_position' => $key
        ]);
      } else {
        $this->{$model}->update([
          'id' => $file->id,
          'sort_position' => $key
        ]);
      }
    }
  }
}