﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Web.Http;
using Core.Captcha;
using Core.Common;
using Core.Common.Links;
using Core.Models;
using Makaba.Entities;
using Makaba.Services.Url;

namespace Makaba {
    public class EntityMapper {
        private UrlService UrlService { get; } = UrlService.Instance;

        public Thread MapThread(ThreadEntity entity, ThreadLink link) {
            var posts = entity.Posts.Select((p, i) => {
                if (i != 0) {
                    p.Sticky = 0;
                }

                return MapPost(p);
            }).ToList();

            return new Thread(
                posts,
                link.BoardId,
                link.ThreadNumber);
        }

        public IList<Post> MapPosts(IEnumerable<PostEntity> entities, string boardId) {
            return entities.Select(MapPost).ToList();
        }

        public Post MapPost(PostEntity source) {
            return new Post(
                source.Num,
                source.Parent,
                source.Subject,
                source.Name,
                source.Trip,
                GetEmail(source.Email),
                source.Comment,
                source.IsOp,
                source.IsBanned,
                source.IsClosed,
                source.Sticky != 0,
                GetFiles(source),
                DateUtils.TimestampToDateTime(source.Timestamp));
        }

        private static string GetEmail(string email) {
            const string garbage = "mailto:";
            var index = email.IndexOf(garbage, StringComparison.OrdinalIgnoreCase);
            return index != -1 ? email.Substring(garbage.Length).Trim() : email;
        }

        private IList<Attachment> GetFiles(PostEntity source) {
            if (source.Files == null)
                return new List<Attachment>();

            return source.Files.Select(f =>
                new Attachment(
                    f.Name,
                    f.Path,
                    UrlService.GetFullUrl(f.Path).AbsoluteUri,
                    f.Size,
                    f.Width,
                    f.Height,
                    f.Thumbnail,
                    UrlService.GetFullUrl(f.Thumbnail).AbsoluteUri,
                    f.ThumbnailWidth,
                    f.ThumbnailHeight,
                    ((int)f.Type) < 0 ? AttachmentType.File : (AttachmentType)f.Type)).ToList();
        }

        private static BoardsCategory MapCategory(BoardsCategoryEntity source) {
            var boards = source.Boards.Select(entity =>
                new BriefBoardInfo(
                    entity.BumpLimit,
                    entity.DefaultName,
                    entity.IsLikesEnabled,
                    entity.IsPostingEnabled,
                    entity.IsThreadTagsEnabled,
                    entity.IsSageEnabled,
                    entity.IsTripCodesEnabled,
                    entity.Id, entity.Name,
                    source.Name == "Взрослым" || entity.Id.EqualsNc("b")));

            return new BoardsCategory(source.Name, boards.ToList());
        }

        public IList<BoardsCategory> MapCategories(IEnumerable<BoardsCategoryEntity> source) {
            return source.Select(MapCategory).ToList();
        }

        public async Task<IHttpContent> MapPostInfo(PostInfo info, string boardId, long parent) {
            var builder = MultipartFormBuilder.Create()
                .AddString("json", 1)
                .AddString("task", "post")
                .AddString("board", boardId)
                .AddString("thread", parent)
                .AddString("email", info.EMail)
                .AddString("name", info.Name)
                .AddString("subject", info.Subject)
                .AddString("op_mark", info.IsOp ? "1" : "0")
                .AddString("comment", info.Text);
            SetupCaptcha(info, builder);

            if (info.Files == null)
                return builder.GetContent();

            var imageIndex = 0;
            foreach (var file in info.Files) {
                await builder.AddFile(file.StreamReference, $"image{imageIndex}", file.Name);
                ++imageIndex;
            }

            return builder.GetContent();
        }

        private void SetupCaptcha(PostInfo info, MultipartFormBuilder builder) {
            if (info.Captcha == null)
                return;
            switch (info.Captcha.Type) {
                case CaptchaServiceType.DvachCaptcha:
                    var c = (DvachCaptcha)info.Captcha;
                    builder
                        .AddString("captcha_type", "2chaptcha")
                        .AddString("2chaptcha_id", c.Key)
                        .AddString("2chaptcha_value", c.Value);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        public PostingResult MapPostingResult(PostingResultEntity entity) {
            return new PostingResult(
                string.IsNullOrWhiteSpace(entity.Error),
                entity.Num ?? entity.Target,
                entity.Reason);
        }

        public BoardPage MapBoardPage(BoardPageEntity entity) {
            return new BoardPage(
                 entity.BoardId,
                 entity.BoardName,
                 entity.BoardInfo,
                 entity.CurrentPage,
                 entity.Pages,
                 entity.Threads.Select(t => CreateThread(t, entity.BoardId)).ToList());
        }

        private BoardThread CreateThread(BoardThreadEntity thread, string boardId) {
            IList<PostEntity> additionalPosts = thread.Posts.Skip(1).ToList();
            var skippedPosts = thread.PostsCount + additionalPosts.Count;
            var skippedPostsWithFiles = thread.FilesCount + additionalPosts.Count(e => e.Files.Count > 0);
            var post = thread.Posts.First();
            return new BoardThread(
                thread.ThreadNum,
                MapPost(post),
                skippedPosts,
                skippedPostsWithFiles);
        }
    }
}