package Slovo::Controller::Stranici;
use Mojo::Base 'Slovo::Controller', -signatures;

use Role::Tiny::With;
with 'Slovo::Controller::Role::Stranica';

# ANY /<page:str>.<lang:lng>.html
# ANY /<page:str>.html
# Display a page in the site
# See the wrapper method 'around execute' in Slovo::Controller::Role::Stranica.
sub execute ($c, $page, $user, $l, $preview) {
  return $c->is_fresh(last_modified => $page->{tstamp})
    ? $c->rendered(304)
    : $c->render();
}

# All the following routes are under /manage

# GET /stranici/create
# Display form for creating resource in table stranici.
sub create ($c) {
  my $str  = $c->stranici;
  my $l    = $c->language;
  my $host = $c->stash('domain')->{domain};
  my $user = $c->user;
  my $pid  = $c->param('pid')
    // $str->all_for_edit($user, $host, $l, {limit => 1, columns => ['id']})->[0]{id};
  my $bread = $str->breadcrumb($pid, $l);
  return $c->render(
    in         => {},
    breadcrumb => $bread,
    parents    => $c->page_id_options($bread, {pid => $pid}, $user, $host, $l),
  );
}

# POST /stranici
# Add a new record to table stranici.
sub store ($c) {
  my $user = $c->user;
  my $in   = {};
  my $str  = $c->stranici;

  # Invoked via OpenAPI
  if ($c->current_route =~ /^api\./) {

    # TODO
    # $c->openapi->valid_input or return;
    # $in = {%{$c->validation->output},%$in};
    # my $id = $str->add($in);
    # $c->res->headers->location($c->url_for("api.show_stranici", id => $id)->to_string);
    return $c->render(openapi => '', status => 201);
  }

  # 1. Validate input
  my $v = $c->_validation;
  $in = $v->output;
  my $host = $c->stash('domain')->{domain};
  if ($v->has_error) {
    my $l = $c->language;
    $in->{pid}
      //= $str->all_for_edit($user, $host, $l, {limit => 1, columns => ['id']})->[0]{id};
    my $bread = $str->breadcrumb($in->{pid}, $l);
    return $c->render(
      status     => 400,
      action     => 'create',
      in         => $in,
      breadcrumb => $bread,
      parents    => $c->page_id_options($bread, {pid => $in->{pid}}, $user, $host, $l),
    );
  }

  # 2. Insert it into the database
  @$in{qw(user_id group_id changed_by)} = ($user->{id}, $user->{group_id}, $user->{id});
  my $id = $str->add($in);

  # 3. Prepare the response data or just return "201 Created"
  # See https://developer.mozilla.org/docs/Web/HTTP/Status/201
  # TODO: make it more user friendly.
  return $c->redirect_to(edit_stranici => {id => $id});
}


# GET /stranici/:id/edit
# Display form for edititing resource in table stranici.
sub edit ($c) {

  my $str = $c->stranici;
  my $l   = $c->language;
  my $row = $str->find_for_edit($c->stash('id'), $l);

  my $domove = $c->domove->all->map(sub { [$_->{site_name} => $_->{id}] });
  my $bread  = $str->breadcrumb($row->{id}, $l);
  my $host   = $c->host_only;

  #TODO: implement language switching based on Ado::L18n
  $c->req->param($_ => $row->{$_}) for keys %$row;    # prefill form fields.
  return $c->render(
    domove     => $domove,
    l          => $l,
    in         => $row,
    breadcrumb => $bread,
    parents    => $c->page_id_options($bread, $row, $c->user, $host, $l),
  );
}

# PUT /stranici/:id
# Update the record in table stranici
sub update ($c) {

  # Validate input
  my $v  = $c->_validation;
  my $in = $v->output;

  # TODO: Make proper error displaying for flash mesages and/or just make sure
  # all stash variables are prepared and error messages are displayed near the
  # failed fields ot in this page.
  if ($v->has_error) {
    $c->flash(message => 'Failed validation for: ' . join(', ', @{$v->failed}));
    return $c->redirect_to('edit_stranici', id => $c->stash->{id});
  }
  @$in{qw(changed_by)} = ($c->user->{id});

  # Update the record
  my $id = $c->param('id');
  $c->stranici->save($id, $in);

  # Redirect to the updated record or just send "204 No Content"
  # See https://developer.mozilla.org/docs/Web/HTTP/Status/204
  my $redirect = $c->param('redirect') // '';
  return $c->redirect_to($redirect, id => $id) if $redirect eq 'show_stranici';
  return $c->render(text => '', status => 204);
}

# GET /stranici/:id
# Display a record from table stranici.
sub show ($c) {
  my $id  = $c->param('id');
  my $row = $c->stranici->find($id);
  if ($c->current_route =~ /^api\./) {    #invoked via OpenAPI
    return $c->render(
      openapi => {errors => [{path => $c->url_for, message => 'Not Found'}]},
      status  => 404
    ) unless $row;
    return $c->render(openapi => $row);
  }
  return $c->render(text     => $c->res->default_message(404), status => 404) unless $row;
  return $c->render(stranici => $row);
}

# GET /stranici
# List resources from table stranici.
## no critic qw(Subroutines::ProhibitBuiltinHomonyms)
sub index ($c) {
  my $str  = $c->stranici;
  my $host = $c->host_only;
  my $v    = $c->validation;

  $v->optional(pid => 'trim')->like(qr/^\d+$/);
  my $in = $v->output;
  my $l  = $c->language;

  if ($c->current_route =~ /^api\./) {    #invoked via OpenAPI
    $c->openapi->valid_input or return;

    # TODO: Modify $in: add where clause, get also title in the requested
    # language from celini and merge it into the stranici object. Modify the
    # Swagger description of response object to conform to the output.
    return $c->render(openapi => $str->all($in));
  }

  return $c->render(host => $host, breadcrumb => $str->breadcrumb($in->{pid}, $l),);
}

# DELETE /stranici/:id
sub remove ($c) {
  if ($c->current_route =~ /^api\./) {    #invoked via OpenAPI
    $c->openapi->valid_input or return;
    my $input = $c->validation->output;
    my $row   = $c->stranici->find($input->{id});
    $c->render(
      openapi => {errors => [{path => $c->url_for, message => 'Not Found'}]},
      status  => 404
      )
      && return
      unless $row;
    $c->stranici->remove($input->{id});
    return $c->render(openapi => '', status => 204);
  }
  my $id = $c->param('id');
  my $v  = $c->validation->input({id => $id});
  $v->required('id');
  $v->error('id' => ['not_writable'])
    unless $c->stranici->find_where(
    {'id' => $id, %{$c->stranici->writable_by($c->user)}});
  my $in = $v->output;
  if ($in->{id}) {
    $c->stranici->remove($in->{id});
  }
  else {
    return !$c->redirect_to(edit_stranici => {id => $c->param('id')});
  }
  return $c->redirect_to('home_stranici');
}

# Validation for actions that store or update
sub _validation ($c) {
  $c->req->param(alias => lc substr(($c->param('title') =~ s/\W//gr), 0, 32))
    unless $c->param('alias');
  for (qw(hidden deleted)) {
    $c->req->param($_ => 0) unless $c->param($_);
  }
  my $v = $c->validation;

  # Add validation rules for the record to be stored in the database
  # If we edit an existing page, check if the page is writable by the
  # current user.
  my $int = qr/^\d{1,10}$/;
  $v->required('pid', 'trim')->like($int);
  $v->optional('dom_id', 'trim')->equals($c->stash('domain')->{id});
  $v->required('alias',     'slugify')->size(0, 32);
  $v->required('page_type', 'trim')->size(0, 32);
  $v->optional('sorting',     'trim')->like($int);
  $v->optional('template',    'not_empty', 'trim')->size(0, 255);
  $v->optional('user_id',     'not_empty', 'trim')->like($int);
  $v->optional('group_id',    'not_empty', 'trim')->like($int);
  $v->optional('permissions', 'trim')->is(\&writable, $c);
  $v->optional('start',       'not_empty')->like($int);
  $v->optional('stop',        'not_empty')->like($int);
  $v->optional('published',   'trim')->in(2, 1, 0);
  $v->optional('hidden',      'trim')->in(1, 0);
  $v->optional('deleted',     'trim')->in(1, 0);
  $v->optional('changed_by',  'not_empty', 'trim')->like($int);

  # Page attributes in celini
  $v->required('title', 'xml_escape', 'trim')->size(3, 32);
  $v->optional('body',     'trim');
  $v->optional('title_id', 'not_empty')->like($int);

  state $languages = $c->languages;
  $v->required('language',    'trim')->in(@$languages);
  $v->required('data_format', 'trim')->in(@{$c->stash->{data_formats}});
  $c->b64_images_to_files('body');
  return $v;
}

# GET /api/stranici
# List of published pages under a given pid in the current domain.
# Used for sidedrawer or sitemap
sub list ($c) {
  state $columns = $c->app->defaults('stranici_columns');
  state $mode    = $c->app->mode;
  $c->openapi->valid_input or return;
  my $in      = $c->validation->output;
  my $user    = $c->user;
  my $preview = $c->is_user_authenticated && $c->param('прегледъ');
  $in->{columns} //= $columns;
  my $list = $c->stranici->all_for_list($user, $c->stash('domain')->{domain},
    $preview, $c->language, $in);
  return $c->render(($mode eq 'development' ? 'openapi' : 'json') => $list);
}

1;