package CloudStore::Store;
use warnings;
use strict;

use autodie;
use JSON::XS;
use File::Path qw(make_path);
use File::Slurp qw();
use Digest::MD5 qw(md5_base64);
use Data::Dump qw(dump);
use Carp qw(confess);
use BerkeleyDB;

use WarnColor;

sub new {
	my ($class) = @_;

	my $self = {};
	bless $self, $class;

	my %md5;
	$self->{db} = tie %md5, 'BerkeleyDB::Hash', -Filename => '/tmp/md5.db', -Flags => DB_CREATE;
	$self->{md5} = \%md5;

	warn "# new ",dump $self if $ENV{DEBUG};

	return $self;
}

sub user_set {
	my ( $self,$data ) = @_;
}

sub user_get {
	my ( $self,$data ) = @_;
}

sub modify_file {
	my ( $self,$data ) = @_;

#	if ( my $old = $self->file_get( $data ) ) {
#		$self->usage_decr( $data );
#	}

	$self->new_file($data);
}

sub new_file {
	my ( $self,$data ) = @_;
#	$self->file_set($data);
#	$self->usage_incr($data);
}

sub remove_file {
	my ( $self, $data ) = @_;
#	$self->usage_decr( $data );
}

sub make_dir {
	my ( $self, $data ) = @_;

}

sub new_link {
	my ( $self, $data ) = @_;

	warn "# new_link ",dump $data;

	if ( $data->{file} =~ m{^(.*/?)\.send/([^/]+)/(.+)$} ) {
		my ( $dir, $to, $name ) = ( $1, $2, $3 );
		my $path = "users/$data->{login}/blob/" . $data->{file};
		my $link_to = readlink $path;
		warn "$link_to";
		if ( $link_to =~ s{^\Q/rsyncd-munged/\E}{/} ) {

			my $s = $path;
			$s =~ s{/[^/]+$}{}; # strip filename
			while ( $link_to =~ s{/../}{/} ) {
				$s =~ s{/[^/]+$}{} || die "can't strip $s";
				warn "## simplify $s $link_to\n";
			}
			$s .= $link_to;

			my $d = "users/$to/blob";
			if ( ! -e $d ) {
				warn "ERROR: no to user $to in $d";
				return;
			}
			$d .= "/$name";

			# $name can contain directories so we must create them
			my $to_dir = $d;
			$to_dir =~ s{/[^/]+$}{};
			make_path $to_dir if ! -e $to_dir;

			if ( ! -e $s ) {
				warn "ERROR: can't find source $s";
			} else {

				warn "link $s -> $d\n";
				link $s, $d;

				my ($l,$f) = ($1,$2) if $s =~ m{users/([^/]+)/blob/(.+)};

#				my $origin = $self->file_get({
#					login => $l,
#					file  => $f,
#				});
#				$self->new_file($origin);
				warn "INFO: sent file ",dump($l,$f);
				my $md5 = $self->{md5}->{$s} || die "no md5 for $s";
				$self->{md5}->{$d} = $md5;
			}


		} else {
			warn "ERROR: can't SEND To:$to Name:$name Link:$link_to";
		}
	}
}

sub transfer {
	my ( $self,$data ) = @_;

	my $blob = "users/$data->{login}/blob";
        my $path = "$blob/$data->{file}";

	if ( $data->{itemize} =~ m/^[c>]([fdL])/ ) { # received change/create
		my $type = $1;

		if ( $type eq 'f' ) {
			$self->modify_file( $data );
			$self->dedup( $data, $path );
		} elsif ( $type eq 'd' ) {
			$self->make_dir( $data );
		} elsif ( $type eq 'L' ) {
			$self->new_link( $data );
		} else {
			die "unknown type $type ", dump $data;
		}
	} elsif ( $data->{itemize} =~ m/\*deleting/ ) {
		$self->remove_file($data);
	}
	return $data;
}

sub md5pool {
	my ( $self, $path, $md5 ) = @_;

	my $pool = 'md5'; # FIXME sharding?
	mkdir $pool unless -e $pool;

	if ( -e "$pool/$md5" ) {
		warn "dedup hit $md5 $path\n";
		my $dedup = $path . '.dedup';
		rename $path, $dedup;
		link "$pool/$md5", $path;
		unlink $dedup;
		# FIXME fix perms?
	} else {
		link $path, "$pool/$md5";
	}

	$self->{md5}->{$path} = $md5;
	warn "++ $md5 $path\n";
}

my $empty_md5 = " " x 32;

sub dedup {
	my ( $self, $data, $path ) = @_;

	if ( $data->{file} =~ /^(.+\/)?md5sum$/ ) {
		my $dir = $1;
		my $imported = 0;
		warn "IMPORT ", $data->{file}, "\n";
		open(my $md5sum, '<', $path);
		while(<$md5sum>) {
			chomp;
			my ( $md5, $file ) = split(/\s+/,$_,2);
			if ( ! -e "md5/$md5" ) {
				warn "MISSING $md5 $file\n";
				next;
			}
			my $new = "users/$data->{login}/blob/$dir$file";
			if ( ! -e $new ) {
				# create path from md5sum file
				my $only_dir = $1 if $new =~ m{^(.+)/[^/]+$};
				make_path $only_dir unless -d $only_dir;
				$imported += link "md5/$md5", $new;
				my $fake = {
					login => $data->{login},
					host => $data->{host},
					file => $dir . $file,
					md5 => $md5,
					size => -s $new,
				};
				$self->new_file($fake);
				warn "import from $path ",dump($fake);
			} else {
				$self->md5pool( $new => $md5 );
			}
		}
		print "INFO imported $imported files from ",dump($data);
	}

	if ( $data->{md5} ne $empty_md5 ) {
		$self->md5pool( $path => $data->{md5} );
	} else {
		warn "empty md5", dump $data;
	}
}

1;
