#!/usr/bin/perl -w
use strict;

use FindBin;
use lib "$FindBin::RealBin/../lib";

use YAML;
use Getopt::Long qw{GetOptions GetOptionsFromArray :config pass_through};

use Bookmarks;

GetOptions(
    'file|f=s' => \my $DBNAME,
);

my $dbname = $DBNAME || $ENV{BKMK_DBNAME} || 'bookmarks.db';
die "Usage: $0 --file <dbname> <command>\n" unless $dbname;

my $bookmarks = Bookmarks->new({
        dbname => $dbname,
    });

my $command = shift;

my %action_for = (
    init => sub {
        my $src_file = shift;
        $bookmarks->create_tables;
        load_bookmarks($src_file) if $src_file;
    },

    get => sub {
        my $identifier = shift;
        my $bookmark = find_bookmark($identifier);
        print $bookmark ? Dump($bookmark->to_hashref) : "Not Found\n";
    },

    add => sub {
        GetOptionsFromArray(
            \@_,
            'title=s' => \my $TITLE,
        );
        my ($uri, @tags) = @_;
        my $title = defined $TITLE ? $TITLE : fetch_title($uri);
        my $bookmark = $bookmarks->add({ uri => $uri, title => $title, tags => \@tags });
        print Dump($bookmark->to_hashref);
    },

    list => sub {
        my @tags = @_;
        my $resources = $bookmarks->search({
                tags => \@tags
            });
        # TODO: list by tags, date, etc.
        # TODO: coordinate this commandline script with the CGI app
        print Dump([ map { $_->to_hashref } @{ $resources->results } ]);
    },

    tag => sub {
        my ($identifier, @tags) = @_;
        my $bookmark = find_bookmark($identifier);
        if ($bookmark) {
            $bookmark->tags(\@tags);
            $bookmarks->update($bookmark);
            print Dump($bookmark->to_hashref);
        } else {
            die "Not found\n";
        }
    },

    # interactive editing of a bookmark
    edit => sub {
        my ($identifier) = @_;
        my $bookmark = find_bookmark($identifier) or die "Not found\n";
        my $editor = $ENV{VISUAL} || $ENV{EDITOR} || 'vi';
        
        # create a temp file and open it in the user's preferred editor
        use File::Temp qw{tempfile};
        my ($fh, $filename) = tempfile(UNLINK => 1);
        YAML::DumpFile($filename, $bookmark->to_hashref);
        system($editor, $filename);

        # abort on empty file
        die "Edit canceled\n" unless -s $filename;

        # update and save the bookmark
        $bookmark = Bookmark->new(YAML::LoadFile($filename));
        $bookmarks->update($bookmark);
        print Dump($bookmark->to_hashref);
    },

    # bulk loading
    load => sub {
        my ($src_file) = @_;
        load_bookmarks($src_file);
    },

    # bulk dumping
    dump => sub {
        my ($dump_file) = @_;
        my $dump = [ map { $_->to_hashref } @{ $bookmarks->search->results } ];
        $dump_file ? YAML::DumpFile($dump_file, $dump) : print Dump($dump);
    },

    # scanning for current status
    scan => sub {
        GetOptionsFromArray(
            \@_,
            'csv'       => \my $CSV,
            'timeout=i' => \my $TIMEOUT,
        );

        require LWP::UserAgent;
        require Text::CSV;

        $TIMEOUT ||= 10;

        my $ua = LWP::UserAgent->new;
        $ua->timeout($TIMEOUT);

        my $csv = Text::CSV->new;

        for my $bookmark (@{ $bookmarks->search->results }) {
            printf "%3d %s\n", $bookmark->id, $bookmark->uri unless $CSV;
            my $response = $ua->head($bookmark->uri);
            printf "   -> %s\n", $response->status_line unless $CSV;
            $csv->combine(
                $bookmark->id,
                $bookmark->uri,
                $response->code,
                $response->message,
            );
            print $csv->string . "\n" if $CSV;
        }
    },
);

$action_for{$command}->(@ARGV);

sub find_bookmark {
    my $identifier = shift;
    my $query = $identifier =~ /^\d+$/ ? { id => $identifier } : { uri => $identifier };
    return $bookmarks->get_bookmark($query);
}

sub fetch_title {
    my $uri = shift;
    require WWW::Mechanize;
    my $mech = WWW::Mechanize->new;
    $mech->get($uri);
    return $mech->title || $uri;
}

sub load_bookmarks {
    my $src_file = shift;
    my $src_bookmarks = YAML::LoadFile($src_file);
    for my $bookmark (@{ $src_bookmarks }) {
        $bookmarks->add($bookmark);
    }
}
