package Test::KorAP::XML::TEI;
use strict;
use warnings;
use Test::More;
use Test::XML::Loy;
use Capture::Tiny qw'capture_stderr';
use Encode 'encode';
use File::Temp qw/tempfile/;
use File::Basename 'dirname';
use File::Spec::Functions qw/catfile/;
use IO::Uncompress::Unzip qw($UnzipError);

use Exporter 'import';

our @EXPORT_OK = qw(korap_tempfile i5_template test_tei2korapxml);

our $data;
unless ($data) {
  $data .= <DATA> while !eof(DATA);
};

# Create a temporary file and file handle
# That will stay intact, if KORAPXMLTEI_DONTUNLINK is set to true.
sub korap_tempfile {
  my $pattern = shift;
  $pattern .= '_' if $pattern;

  # default: remove temp. file created by func. tempfile
  #  to keep temp. files use e.g. 'KORAPXMLTEI_DONTUNLINK=1 prove -lr t/script.t'
  return tempfile(
    'KorAP-XML-TEI_' . ($pattern // '') . 'XXXXXXXXXX',
    SUFFIX => '.tmp',
    TMPDIR => 1,
    UNLINK => $ENV{KORAPXMLTEI_DONTUNLINK} ? 0 : 1
  )
};


# Return basic i5 document with replacable parts.
# Supports:
# - korpusSigle
# - dokumentSigle
# - textSigle
# - text
sub i5_template {
  my %replace = @_;
  my $tpl = $data;

  foreach my $key (keys %replace) {
    $tpl =~ s!<% $key %>!$replace{$key}!ge;
  };

  for ($tpl) {
    s!<% korpusSigle %>!AAA!g;
    s!<% dokumentSigle %>!AAA/BBB!g;
    s!<% textSigle %>!AAA/BBB.00000!g;
    s!<% text %>!Lorem ipsum!g;
  };

  return $tpl;
};

# Construct script test helper object.
# WARNING:
#   This isn't very portable and works only in the context
#   of the test suite.
sub test_tei2korapxml {
  my ($file, $script, $pattern);
  my ($env, $param, $fh) = ('', '');

  if (@_ == 1) {
    $file = shift;
  }

  else {
    my %hash = @_;
    $file    = $hash{file};
    $script  = $hash{script} if $hash{script};
    $param   = $hash{param}  if $hash{param};
    $env     = $hash{env}    if $hash{env};
    $pattern = $hash{tmp}    if $hash{tmp};

    # Handle template instead of hash
    if ($hash{template} && !$file) {
      my $pattern = delete $hash{template}->{pattern};
      my $tmpl = i5_template(%{$hash{template}});

      # Write template to file
      ($fh, $file) = korap_tempfile($pattern);
      print $fh $tmpl;
      close($fh);
    }
  };

  # Assume script in test environment
  unless ($script) {
    $script = catfile(dirname(__FILE__), '..', '..', '..', '..', 'script', 'tei2korapxml');

    unless (-e $script) {
      $script = undef;
    };
  };

  my $call;
  if ($script) {
    $call = "perl '$script'";
  }

  # Take installed path
  else {
    # This may be unoptimal, as it is silent
    $call = 'tei2korapxml';
  };

  # Because of some capturing issues and for debugging purposes
  # we pipe stdout through a temp file.
  my (undef, $fn) = korap_tempfile($pattern);

  $call = "cat '$file' | $env $call $param > $fn";
  my $stderr = capture_stderr { `$call` };

  # Read from written file
  my $stdout = '';
  if (open($fh, '<', $fn)) {
    binmode($fh);
    $stdout .= <$fh> while !eof($fh);
    close($fh);
  };

  # Bless data for inspection
  return bless {
    stdout => $stdout,
    stderr => $stderr
  }, __PACKAGE__;
};


# Set or get success of the last test
sub success {
  my $self = shift;
  if (@_) {
    $self->{success} = shift;
    return $self;
  };
  return $self->{success} // 0;
};


# Check for stderr equality
sub stderr_is {
  my ($self, $value, $desc) = @_;
  return $self->_test(
    'is',
    $self->{stderr},
    $value,
    _desc($desc, 'exact match for stderr')
  );
};


# Check for stderr similarity
sub stderr_like {
  my ($self, $value, $desc) = @_;
  return $self->_test(
    'like',
    $self->{stderr},
    $value,
    _desc($desc, 'similar to stderr')
  );
};


# Check for stderr non-similarity
sub stderr_unlike {
  my ($self, $value, $desc) = @_;
  return $self->_test(
    'unlike',
    $self->{stderr},
    $value,
    _desc($desc, 'not similar to stderr')
  );
};


# Check if a zip exists
sub file_exists {
  my ($self, $file, $desc) = @_;

  my $exists;
  if (my $zip = IO::Uncompress::Unzip->new(\$self->{stdout}, Name => $file)) {
    $exists = 1;
  };

  return $self->_test(
    'ok',
    $exists,
    _desc($desc, "File $file exists in zip file")
  );
};


# Check if a zip does not exist
sub file_exists_not {
  my ($self, $file, $desc) = @_;

  my $exists = 1;
  if (my $zip = IO::Uncompress::Unzip->new(\$self->{stdout}, Name => $file)) {
    $exists = 0;
  };

  return $self->_test(
    'ok',
    $exists,
    _desc($desc, "File $file does not exist in zip file")
  );
};


# Check if a zip exists
sub file_readable {
  my ($self, $file, $desc) = @_;

  my $readable;
  if (my $zip = IO::Uncompress::Unzip->new(\$self->{stdout}, Name => $file)) {
    $readable = !$zip->eof;
  };

  return $self->_test(
    'ok',
    $readable,
    _desc($desc, "File $file exists in zip file and is readable")
  );
};


# Get content of a file
sub get_content_of {
  my ($self, $file) = @_;
  if (my $zip = IO::Uncompress::Unzip->new(\$self->{stdout}, Name => $file)) {
    my $data = '';
    $data .= $zip->getline while !$zip->eof;
    $zip->close;
    return $data;
  };
  return '';
};


# Returns an Test::XML::Loy object
sub unzip_xml {
  my ($self, $file) = @_;
  if (my $zip = IO::Uncompress::Unzip->new(\$self->{stdout}, Name => $file)) {
    my $data = '';
    $data .= $zip->getline while !$zip->eof;
    $zip->close;

    return Test::XML::Loy->new($data);
  };

  $self->_test('ok', 0, qq!Unable to unzip "$file"!);
  return;
};


sub _test {
  my ($self, $name, @args) = @_;
  local $Test::Builder::Level = $Test::Builder::Level + 2;
  return $self->success(!!Test::More->can($name)->(@args));
};


sub _desc {
  encode 'UTF-8', shift || shift;
};


1;


__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE idsCorpus
  PUBLIC
  "-//IDS//DTD IDS-I5 1.0//EN"
  "http://corpora.ids-mannheim.de/I5/DTD/i5.dtd">
<idsCorpus>
  <idsHeader type="corpus">
    <fileDesc>
      <titleStmt>
        <korpusSigle><% korpusSigle %></korpusSigle>
      </titleStmt>
    </fileDesc>
  </idsHeader>
  <idsDoc version="1.0">
    <idsHeader type="document">
      <fileDesc>
        <titleStmt>
          <dokumentSigle><% dokumentSigle %></dokumentSigle>
        </titleStmt>
      </fileDesc>
    </idsHeader>
    <idsText version="1.0">
      <idsHeader type="text">
        <fileDesc>
          <titleStmt>
            <textSigle><% textSigle %></textSigle>
          </titleStmt>
        </fileDesc>
      </idsHeader>
      <text>
        <% text %>
      </text>
    </idsText>
  </idsDoc>
</idsCorpus>
__END__
