package CloudStore::Couchbase;
use warnings;
use strict;

use autodie;
use JSON::XS;
use File::Path qw(make_path);
use File::Slurp qw();
use Cache::Memcached;
use Digest::MD5 qw(md5_base64);
use Data::Dump qw(dump);

my $buckets = {
	files => 5800,
	usage => 5801,
};

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

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

	foreach my $bucket ( keys %$buckets ) {
		my $port = $buckets->{$bucket};
		my $server = new Cache::Memcached {
			'servers' => [ "127.0.0.1:$port" ],
			'debug' => $ENV{DEBUG},
		#	'compress_threshold' => 10_000,
		};
		#$server->set_servers($array_ref);
		#$server->set_compress_threshold(10_000);
		$server->enable_compress(0);
		$self->{$bucket} = $server;

	}

	warn "# new ",dump $self;

	return $self;
}

sub usage_decr {
	my ($self,$data) = @_;
	$self->{usage}->decr( $data->{login} => $data->{size} );
}

sub usage_incr {
	my ($self,$data) = @_;
	$self->{usage}->incr( $data->{login} => $data->{size} );
}

sub usage {
	my ($self,$data) = @_;
	$self->{usage}->get( $data->{login} );
}

sub _key {
	my $data = shift;
	#md5_base64( $data->{login} . '/' . $data->{file} );
	$data->{login} . ':' . $data->{file};
}

sub file_set {
	my ($self,$data) = @_;
	my $k = _key $data;
	my $json = encode_json $data;
	$self->{files}->set( $k => $json );
	return $json;
}

sub file_get {
	my ($self,$data) = @_;
	my $k = _key $data;
	if ( my $json = $self->{files}->get($k) ) {
		return decode_json $json;
	}
}

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 );
	my $k = _key $data;
	$self->{files}->delete( $k );
}

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

}

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

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

	if ( $data->{itemize} =~ m/^[c>]([fd])/ ) { # 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 );
		} else {
			die "unknown type $type ", dump $data;
		}
	} elsif ( $data->{itemize} =~ m/\*deleting/ ) {
		$self->remove_file($data);
	}
	return $data;
}

sub md5pool {
	my ( $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;
	} else {
		link $path, "$pool/$md5";
	}
}

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 "fake ",dump($fake);
			} else {
				md5pool $new => $md5;
			}
		}
		print "INFO imported $imported files from ",dump($data);
	}

	if ( $data->{md5} ne $empty_md5 ) {
		md5pool $path => $data->{md5};
	} else {
		
	}
}

1;
