package SL::File::Backend::Webdav;

use strict;

use parent qw(SL::File::Backend);
use SL::DB::File;

use SL::System::Process;
use File::Copy;
use File::Slurp;
use File::Basename;
use File::Path qw(make_path);
use File::MimeInfo::Magic;
use File::stat;

#
# public methods
#

sub delete {
  my ($self, %params) = @_;
  $main::lxdebug->message(LXDebug->DEBUG2(), "del in backend " . $self . "  file " . $params{dbfile});
  $main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $params{dbfile}->id * 1);
  return 0 unless $params{dbfile};
  my ($file_path, undef, undef) = $self->webdav_path($params{dbfile});
  unlink($file_path);
  return 1;
}

sub rename {
  my ($self, %params) = @_;
  return 0 unless $params{dbfile};
  my (undef, $oldwebdavname) = split(/:/, $params{dbfile}->location, 2);
  my ($tofile, $basepath, $basename) = $self->webdav_path($params{dbfile});
  my $fromfile = File::Spec->catfile($basepath, $oldwebdavname);
  $main::lxdebug->message(LXDebug->DEBUG2(), "renamefrom=" . $fromfile . " to=" . $tofile);
  move($fromfile, $tofile);
}

sub save {
  my ($self, %params) = @_;
  die 'dbfile not exists' unless $params{dbfile};
  $main::lxdebug->message(LXDebug->DEBUG2(), "in backend " . $self . "  file " . $params{dbfile});
  $main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $params{dbfile}->id);
  my $dbfile = $params{dbfile};
  die 'no file contents' unless $params{file_path} || $params{file_contents};

  if ($params{dbfile}->id * 1 == 0) {

    # new element: need id for file
    $params{dbfile}->save;
  }
  my ($tofile, undef, $basename) = $self->webdav_path($params{dbfile});
  if ($params{file_path} && -f $params{file_path}) {
    copy($params{file_path}, $tofile);
  }
  elsif ($params{file_contents}) {
    open(OUT, "> " . $tofile);
    print OUT $params{file_contents};
    close(OUT);
  }
  return 1;
}

sub get_version_count {
  my ($self, %params) = @_;
  die "no dbfile" unless $params{dbfile};
  ## TODO
  # Webdav doesn't have versions by now. And delete checks for version, so return 0 for now
  return 0;
}

sub get_mtime {
  my ($self, %params) = @_;
  die "no dbfile" unless $params{dbfile};
  $main::lxdebug->message(LXDebug->DEBUG2(), "version=" .$params{version});
  my ($path, undef, undef) = $self->webdav_path($params{dbfile});
  die "No file found in Backend: " . $path unless -f $path;
  my $dt = DateTime->from_epoch(epoch => stat($path)->mtime, time_zone => $::locale->get_local_time_zone()->name)->clone();
  $main::lxdebug->message(LXDebug->DEBUG2(), "dt=" .$dt);
  return $dt;
}

sub get_filepath {
  my ($self, %params) = @_;
  die "no dbfile" unless $params{dbfile};
  my ($path, undef, undef) = $self->webdav_path($params{dbfile});
  die "No file found in Backend: " . $path unless -f $path;
  return $path;
}

sub get_content {
  my ($self, %params) = @_;
  my $path = $self->get_filepath(%params);
  return "" unless $path;
  my $contents = File::Slurp::read_file($path);
  return \$contents;
}

sub sync_from_backend {
  my ($self, %params) = @_;
  return unless $params{file_type};

  $self->sync_all_locations(%params);

}

sub enabled {
  return $::instance_conf->get_doc_webdav;
}

#
# internals
#

my %type_to_path = (
  sales_quotation             => 'angebote',
  sales_order                 => 'bestellungen',
  request_quotation           => 'anfragen',
  purchase_order              => 'lieferantenbestellungen',
  sales_delivery_order        => 'verkaufslieferscheine',
  purchase_delivery_order     => 'einkaufslieferscheine',
  purchase_reclamation        => 'einkaufsreklamation',
  sales_reclamation           => 'verkaufsreklamation',
  supplier_delivery_order     => 'beistelllieferscheine',
  rma_delivery_order          => 'retourenlieferscheine',
  credit_note                 => 'gutschriften',
  invoice                     => 'rechnungen',
  invoice_for_advance_payment => 'rechnungen',
  final_invoice               => 'rechnungen',
  purchase_invoice            => 'einkaufsrechnungen',
  part                        => 'waren',
  service                     => 'dienstleistungen',
  assembly                    => 'erzeugnisse',
  letter                      => 'briefe',
  general_ledger              => 'dialogbuchungen',
  gl_transaction              => 'dialogbuchungen',
  accounts_payable            => 'kreditorenbuchungen',
  shop_image                  => 'shopbilder',
  customer                    => 'kunden',
  vendor                      => 'lieferanten',
);

my %type_to_model = (
  sales_quotation             => 'Order',
  sales_order                 => 'Order',
  request_quotation           => 'Order',
  purchase_order              => 'Order',
  sales_delivery_order        => 'DeliveryOrder',
  purchase_delivery_order     => 'DeliveryOrder',
  sales_reclamation           => 'Reclamation',
  purchase_reclamation        => 'Reclamation',
  supplier_delivery_order     => 'DeliveryOrder',
  rma_delivery_order          => 'DeliveryOrder',
  credit_note                 => 'Invoice',
  invoice                     => 'Invoice',
  invoice_for_advance_payment => 'Invoice',
  final_invoice               => 'Invoice',
  purchase_invoice            => 'PurchaseInvoice',
  part                        => 'Part',
  service                     => 'Part',
  assembly                    => 'Part',
  letter                      => 'Letter',
  general_ledger              => 'GLTransaction',
  gl_transaction              => 'GLTransaction',
  accounts_payable            => 'GLTransaction',
  shop_image                  => 'Part',
  customer                    => 'Customer',
  vendor                      => 'Vendor',
);

my %model_to_number = (
  Order           => 'ordnumber',
  DeliveryOrder   => 'ordnumber',
  Reclamation     => 'record_number',
  Invoice         => 'invnumber',
  PurchaseInvoice => 'invnumber',
  Part            => 'partnumber',
  Letter          => 'letternumber',
  GLTransaction   => 'reference',
  ShopImage       => 'partnumber',
  Customer        => 'customernumber',
  Vendor          => 'vendornumber',
);

sub webdav_path {
  my ($self, $dbfile) = @_;

  #die "No webdav backend enabled" unless $::instance_conf->get_webdav;

  my $type = $type_to_path{ $dbfile->object_type };

  die "Unknown type" unless $type;

  my $number = $dbfile->backend_data;
  if ($number eq '') {
    $number = $self->_get_number_from_model($dbfile);
    $dbfile->backend_data($number);
    $dbfile->save;
  }
  $number =~ s/\//-/g; # replace forbidden char;
  $main::lxdebug->message(LXDebug->DEBUG2(), "file_name=" . $dbfile->file_name ." number=".$number);

  my @fileparts = split(/_/, $dbfile->file_name);
  my $number_ext = pop @fileparts;
  my ($maynumber, $ext) = split(/\./, $number_ext, 2);
  push @fileparts, $maynumber if $maynumber ne $number;

  my $basename = join('_', @fileparts);

  my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id}, $type, $number);
  if (!-d $path) {
    File::Path::make_path($path, { chmod => 0770 });
  }
  my $fname = $basename . '_' . $number . '_' . $dbfile->itime->strftime('%Y%m%d_%H%M%S');
  $fname .= '.' . $ext if $ext;

  $main::lxdebug->message(LXDebug->DEBUG2(), "webdav path=" . $path . " filename=" . $fname);

  return (File::Spec->catfile($path, $fname), $path, $fname);
}

sub get_rootdir { SL::System::Process::exe_dir() }

sub _get_number_from_model {
  my ($self, $dbfile) = @_;

  my $class = 'SL::DB::' . $type_to_model{ $dbfile->object_type };
  eval "require $class";
  my $obj = $class->new(id => $dbfile->object_id)->load;
  die 'no object found' unless $obj;
  my $numberattr = $model_to_number{ $type_to_model{ $dbfile->object_type } };
  return $obj->$numberattr;
}

#
# TODO not fully imlemented and tested
#
sub sync_all_locations {
  my ($self, %params) = @_;

  my %dateparms = (dateformat => 'yyyymmdd');

  foreach my $type (keys %type_to_path) {

    my @query = (
      file_type => $params{file_type},
      object_type    => $type
    );
    my @oldfiles = @{ SL::DB::Manager::File->get_all(
        query => [
          file_type => $params{file_type},
          object_type    => $type
        ]
      )
    };

    my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id},$type_to_path{$type});

    if (opendir my $dir, $path) {
      foreach my $file (sort { lc $a cmp lc $b }
        map { decode("UTF-8", $_) } readdir $dir)
      {
        next if (($file eq '.') || ($file eq '..'));

        my $fname = $file;
        $fname =~ s|.*/||;

        my ($filename, $number, $date, $time_ext) = split(/_/, $fname);
        my ($time, $ext) = split(/\./, $time_ext, 2);

        $time = substr($time, 0, 2) . ':' . substr($time, 2, 2) . ':' . substr($time, 4, 2);

        #my @found = grep { $_->backend_data eq $fname } @oldfiles;
        #if (scalar(@found) > 0) {
        #  @oldfiles = grep { $_ != @found[0] } @oldfiles;
        #}
        #else {
          my $dbfile = SL::DB::File->new();
          my $class  = 'SL::DB::Manager::' . $type_to_model{$type};
          my $obj =
            $class->find_by(
            $model_to_number{ $type_to_model{$type} } => $number);
          if ($obj) {

            my $mime_type = File::MimeInfo::Magic::magic(File::Spec->catfile($path, $fname));
            if (!$mime_type) {
              # if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
              $mime_type = File::MimeInfo::Magic::mimetype($fname);
              $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
            }

            $dbfile->assign_attributes(
              object_id   => $obj->id,
              object_type => $type,
              source      => $params{file_type} eq 'document' ? 'created' : 'uploaded',
              file_type   => $params{file_type},
              file_name   => $filename . '_' . $number . '_' . $ext,
              mime_type   => $mime_type,
              itime       => $::locale->parse_date_to_object($date . ' ' . $time, %dateparms),
            );
            $dbfile->save;
          }
        #}

        closedir $dir;
      }
    }
  }
}

1;

__END__

=pod

=encoding utf8

=head1 NAME

SL::File::Backend::Filesystem  - Filesystem class for file storage backend

=head1 SYNOPSIS

See the synopsis of L<SL::File::Backend>.

=head1 OVERVIEW

This specific storage backend use a Filesystem which is only accessed by this interface.
This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
to store the files not to flat in the filesystem.


=head1 METHODS

See methods of L<SL::File::Backend>.

=head1 SEE ALSO

L<SL::File::Backend>

=head1 TODO

The synchronization must be tested and a periodical task is needed to synchronize in some time periods.

=head1 AUTHOR

Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>

=cut
