# -*- cperl -*-
# -----------------------------------------------------------------------------
package Celaenito::Httpd::URLInfo;
use strict;
use warnings;
use HTTP::Status;
use URI::QueryParam;
use POE::Kernel;
use HTML::Entities;
use Celaenito::DB;
use Celaenito::Template;
use Celaenito::Permission;
use Celaenito::HyperEstraier;
use Celaenito::StaticFile;
use Celaenito::Config;
use Celaenito::FileStorage;
use Celaenito::Curi;
use Celaenito::Httpd;
use Pager;
use Kibibyte qw(kibibyte_format);
use SQLTime;

sub handler {
    my ($req, $resp) = @_;

    if ($req->uri->path eq '/-/info') {
	my $mode = $req->uri->query_param('mode');
	if ($mode and $mode eq 'action') {
	    return action_handler(@_);
	}
	else {
	    return info_handler(@_);
	}
    }
    else {
	return notfound_handler(@_);
    }
}

sub info_handler {
    my ($req, $resp) = @_;
    my $urlid = $req->uri->query_param('urlid');
    
    not CAN_SEE($req) and
	return need_auth_handler(@_);

    my $t = {};

    my $url_info = DB->selectrow_hashref(q{
	SELECT *
	  FROM target_url
	 WHERE urlid = ?
    }, undef, $urlid);
    if (!$url_info) {
	die "URL ID `$urlid' does not exist";
    }
    my %opts = %{ eval($url_info->{options} || '{}') };

    # Configuration and Statistics
    
    if ($_ = $opts{auto_update_interval}) {
	local *split_interval = \&Celaenito::Httpd::Index::split_interval;
	($t->{au_day}, $t->{au_hour}, $t->{au_min}) = split_interval($_);
	$t->{au_keep_dis} = $opts{keep_update_on_disappear};
	$t->{au_inherit} = $opts{inherit_auto_update};
    }

    if ($opts{recursive_fetch}) {
	$t->{rcr_level} = $opts{recursion_level};
	$t->{rcr_no_par} = $opts{no_parent};
	$t->{rcr_span_hosts} = $opts{span_hosts};
    }

    my $reserved_job = DB->selectrow_hashref(q{
	SELECT *
	  FROM job_queue
	 WHERE urlid = ? AND status = "wait"
    }, undef, $urlid);
    if ($reserved_job) {
	$t->{reserved_time} = sqlmaketime(
	    sqlparsetime($reserved_job->{begin_time}));
    }

    # Versions

    my $pager = Pager->new(
	fetch => {
	    sql => q{
		SELECT version
		  FROM versions
		 WHERE urlid = ?
		 ORDER BY version DESC
	    },
	    placeholder => [$urlid],
	    dbh => DB,
	},
	page_to_show   => scalar($req->uri->query_param('page')) || 1,
	pagenums_limit => 10,
	items_per_page => scalar($req->uri->query_param('limit')) || 20,
	show_meta => sub {
	    my %meta = @_;
	    $t->{vers} = {
		page      => $meta{page},
		last_page => $meta{last_page},
		pages     => [$meta{pagelink_begin} .. $meta{pagelink_end}],
		entries   => [],
	    };
	},
	show_content => sub {
	    my $row = shift;

	    my $ent = {};
	    push @{$t->{vers}{entries}}, $ent;

	    my $info = FS->get_info(
		$urlid, $row->{version});

	    $ent->{celaenito_url} =
	      celaenito_uri(
		  host_uri => $req->uri,
		  orig_uri => $url_info->{url},
		  time     => curi_maketime(
		      sqlparsetime($info->{timestamp})),
		 )->as_string;
	    $ent->{type}         = $info->{type};
	    $ent->{timestamp}    = sqlmaketime(sqlparsetime($info->{timestamp}));
	    $ent->{size}         = kibibyte_format('%.2f %s', $info->{data_size});
	    $ent->{content_type} = $info->{content_type} || 'unknown';
	    $ent->{refcount}     = $info->{refcount};
	    $ent->{version}      = $info->{version};
	},
       );
    $pager->execute;

    # Links
    if (defined(my $ver = FS->get_latest_version($urlid))) {
	my $info = FS->get_info($urlid, $ver);
	my $links = FS->get_links($urlid, $ver);
	
	$t->{links} = [];
	foreach my $link (@$links) {
	    my $absuri = URI->new_abs(
		$link->[2], $link->[3] || $url_info->{url});

	    my $ent = {};
	    push @{$t->{links}}, $ent;

	    $ent->{original_url} = $absuri->as_string;

	    my $canon = do {
		$_ = $absuri->clone;
		$_->fragment(undef);
		$_->canonical;
	    };

	    ($ent->{urlid}) = DB->selectrow_array(q{
                  SELECT urlid
                    FROM target_url
                   WHERE url = ?
              }, undef, $canon->as_string);

	    if ($_ = $ent->{urlid}) {
		if (defined FS->get_latest_version($_)) {
		    $ent->{celaenito_url} =
		      celaenito_uri(
			  host_uri => $req->uri,
			  orig_uri => $absuri,
			  time     => 'latest',
			 );
		}
	    }
	}
    }

    $t->{title} = config('ui.title');
    $t->{celaenito_url} = celaenito_uri(
	host_uri => $req->uri,
	orig_uri => $url_info->{url},
	time     => 'latest',
       )->as_string;
    $t->{original_url}  = $url_info->{url};
    $t->{priority}      = $url_info->{priority};
    $t->{label}         = encode_entities($url_info->{label}, "<&>");
    $t->{notation}      = do {
	join '<br />',
	  map {
	      encode_entities($_, "<&>");
	  } split /\n/, ($url_info->{notation} || '');
    };
    $t->{estraier}      = defined EST;
    $t->{added_date}    = sqlmaketime(sqlparsetime($url_info->{added_date}));
    $t->{last_modified} = sqlmaketime(sqlparsetime($url_info->{last_modified}));
    $t->{versions}      = do {
	scalar FS->versions_list($url_info->{urlid});
    };
    $t->{size}          = do {
	my $size = 0;
	foreach my $ver (FS->versions_list($url_info->{urlid})) {
	    $size += FS->get_size($url_info->{urlid}, $ver);
	}
	kibibyte_format '%.2f %s', $size;
    };
    $t->{overwrite}     = $opts{overwrite};
    $t->{au}            = $opts{auto_update_interval};
    $t->{rcr}           = $opts{recursive_fetch};
    $t->{urlid}         = $url_info->{urlid};

    my $out = '';
    TMPL->process('urlinfo.tt', $t, \$out)
      or die Template->error;
    
    $resp->content($out);
    return RC_OK;
}

sub action_handler {
    my ($req, $resp) = @_;
    my $urlid = $req->uri->query_param('urlid');

    my $redirect = $req->uri->clone;
    $redirect->fragment(undef);
    
    if ($req->uri->query_param('edit')) {
	CAN_EDIT($req) or
	  return need_auth_handler(@_);
	
	$redirect->path('/-/edit');
	$redirect->query(undef);
	$redirect->query_param(urlid => $urlid);
    }
    elsif ($req->uri->query_param('update')) {
	CAN_UPDATE($req) or
	  return need_auth_handler(@_);
	
	$poe_kernel->post(
	    jobqueue => enqueue => $urlid, time);

	$redirect->path('/-/info');
	$redirect->query(undef);
	$redirect->query_param(urlid => $urlid);
    }
    elsif ($req->uri->query_param('delete')) {
	return delete_handler(@_);
    }
    elsif ($req->uri->query_param('cleanup')) {
	return cleanup_handler(@_);
    }
    else {
	# delver:を探す
	my $ver;
	foreach (keys %{ $req->uri->query_form_hash }) {
	    /^delver:(\d+)$/ or next;
	    $ver = $1;
	    last;
	}
	
	if ($ver) {
	    return delver_handler(@_, $ver);
	}
	else {
	    die "parameter error";
	}
    }
    
    $resp->header(Location => $redirect->as_string);
    return RC_SEE_OTHER;
}

sub delete_handler {
    my ($req, $resp) = @_;
    my $urlid = $req->uri->query_param('urlid');

    CAN_DEL($req) or
      return need_auth_handler(@_);

    if (!$req->uri->query_param('nowarn')) {
	return Celaenito::Httpd::General::confirm(
	    response => $resp,
	    message  => 'Do you really want to delete this URL?',
	    ok       => do {
		$_ = $req->uri->clone;
		$_->query_param(nowarn => 1);
		$_;
	    },
	    cancel   => do {
		$_ = $req->uri->clone;
		$_->query(undef);
		$_->query_param(urlid => $urlid);
		$_;
	    },
	   );
    }

    # 實行中のジョブがあれば終了させる
    $poe_kernel->post(jobqueue => stop_agent => $urlid);

    # 削除
    FS->delete_url($urlid);
    EST and EST->delete(do {
	($_) = DB->selectrow_array(q{
            SELECT url
              FROM target_url
             WHERE urlid = ?
        }, undef, $urlid);
	$_;
    });

    DB->do(q{
	LOCK TABLES
	  target_url WRITE,
	  job_queue  WRITE
    });

    DB->do(q{DELETE FROM target_url WHERE urlid = ?}, undef, $urlid);
    DB->do(q{DELETE FROM job_queue WHERE urlid = ?}, undef, $urlid);

    DB->do(q{UNLOCK TABLES});

    # URL一覧にリダイレクト
    return redirect_handler(
	$req, $resp, '/-/list');
}

sub cleanup_handler {
    my ($req, $resp) = @_;
    my $urlid = $req->uri->query_param('urlid');

    CAN_DEL($req) or
      return need_auth_handler(@_);

    if (!$req->uri->query_param('nowarn')) {
	return Celaenito::Httpd::General::confirm(
	    response => $resp,
	    message  => 'Do you really want to delete versions of this URL except the latest one?',
	    ok       => do {
		$_ = $req->uri->clone;
		$_->query_param(nowarn => 1);
		$_;
	    },
	    cancel   => do {
		$_ = $req->uri->clone;
		$_->query(undef);
		$_->query_param(urlid => $urlid);
		$_;
	    },
	   );
    }

    # 削除
    FS->delete_old_versions($urlid);

    # URL情報にリダイレクト
    return redirect_handler(
	$req, $resp, '/-/info', {urlid => $urlid});
}

sub delver_handler {
    my ($req, $resp, $ver) = @_;
    my $urlid = $req->uri->query_param('urlid');

    CAN_DEL($req) or
      return need_auth_handler(@_);

    if (!$req->uri->query_param('nowarn')) {
	return Celaenito::Httpd::General::confirm(
	    response => $resp,
	    message  => 'Do you really want to delete this version of the URL?',
	    ok       => do {
		$_ = $req->uri->clone;
		$_->query_param(nowarn => 1);
		$_;
	    },
	    cancel   => do {
		$_ = $req->uri->clone;
		$_->query(undef);
		$_->query_param(urlid => $urlid);
		$_;
	    },
	   );
    }

    # 削除
    FS->delete_version($urlid, $ver);

    # target_url を更新
    DB->do(q{
        UPDATE target_url
           SET last_modified = NULL
          WHERE urlid = ?
    }, undef, $urlid);

    # URL情報にリダイレクト
    return redirect_handler(
	$req, $resp, '/-/info', {urlid => $urlid});
}

1;
