package Text::vCard::Precisely::V3::Node;

use Carp;
use Encode;

use 5.12.5;
use Text::LineFold;

use overload(
    '""' => \&as_string
);

use Moose;
use Moose::Util::TypeConstraints;

enum 'Name' => [qw( FN N SORT_STRING
    ADR LABEL TEL EMAIL PHOTO LOGO URL
    TZ GEO NICKNAME KEY NOTE
    ORG TITLE ROLE CATEGORIES
    SOURCE SOUND
    X-SOCIALPROFILE
)];
has name => ( is => 'rw', required => 1, isa => 'Name' );

subtype 'Content'
    => as 'Str'
    => where { use utf8; decode_utf8($_) =~  m|^[\w\p{ascii}\s]*$|s }   # Does it need to be more strictly?
    => message { "The value you provided, $_, was not supported" };
has content => ( is => 'rw', required => 1, isa => 'Content' );

subtype 'Preffered'
    => as 'Int'
    => where { $_ > 0 and $_ <= 100 }
    => message { "The number you provided, $_, was not supported in 'Preffered'" };
has pref => ( is => 'rw', isa => 'Preffered' );

subtype 'Type'
    => as 'Str'
    => where {
        m/^(:?work|home|PGP)$/is or #common
        m|^(:?[a-zA-z0-9\-]+/X-[a-zA-z0-9\-]+)$|s; # does everything pass?
    }
    => message { "The text you provided, $_, was not supported in 'Type'" };
has types => ( is => 'rw', isa => 'ArrayRef[Type]', default => sub{[]} );

subtype 'Language'
    => as 'Str'
    => where { m|^[a-z]{2}(:?-[a-z]{2})?$|s }  # does it need something strictly?
    => message { "The Language you provided, $_, was not supported" };
has language => ( is => 'rw', isa =>'Language' );

sub charset {    # DEPRECATED in vCard 3.0
    my $self = shift;
    croak "'CHARSET' param is DEPRECATED! vCard3.0 will accept just ONLY UTF-8";
}

__PACKAGE__->meta->make_immutable;
no Moose;

sub as_string {
    my ($self) = @_;
    my @lines;
    my $node = $self->name();
    $node =~ tr/_/-/;

    push @lines, uc($node) || croak "Empty name";
    push @lines, 'TYPE=' . join( ',', map { uc $_ } @{ $self->types } ) if @{ $self->types || [] } > 0;
    push @lines, 'PREF=' . $self->pref if $self->pref;
    push @lines, 'LANGUAGE=' . $self->language if $self->language;

    my $string = join(';', @lines ) . ':' . (
        ref $self->content eq 'Array'?
        map{ $node =~ /^(:?LABEL|GEO)$/s? $self->content : $self->_escape($_) } @{ $self->content }:
        $node =~ /^(:?LABEL|GEO)$/s? $self->content: $self->_escape( $self->content )
    );
    return $self->fold($string);
}

sub fold {
    my $self = shift;
    my $string = shift;
    my %arg = @_;
    my $lf = Text::LineFold->new( CharMax => 74, Newline => "\x0D\x0A", TabSize => 1 );   # line break with 75bytes
    my $decoded = decode_utf8($string);

    $string =~ s/(?<!\r)\n/\t/g;

    $string = $decoded =~ /\P{ascii}+/ || $arg{-force}?
        $lf->fold( "", " ", $string ):
        $lf->fold( "", "  ", $string );

    $string =~ tr/\t/\n/;

    return $string;
}

sub _escape {
    my $self = shift;
    my $txt = shift;
    ( my $r = $txt ) =~ s/([,;\\])/\\$1/sg if $txt;
    return $r || '';
}

1;
