package document

import (
	"context"
	"fmt"
	"time"

	lark "github.com/larksuite/oapi-sdk-go/v3"
	larkdocx "github.com/larksuite/oapi-sdk-go/v3/service/docx/v1"
)

type DocumentBuilder interface {
	AppendTitleBlock(ctx context.Context, titleString string) error
	AppendPlainTextBlock(ctx context.Context, text string) error
	AppendMarkdownTextBlock(ctx context.Context, markdownText string) error
	AppendTextTable(ctx context.Context, rows uint, cols uint, texts []string) error
	Create(ctx context.Context) (documentId string, err error)
}

type defaultDocumentBuilder struct {
	larkcli        *lark.Client
	reqBodyBuilder *larkdocx.CreateDocumentReqBodyBuilder
	blocks         []*larkdocx.Block
	title          string
}

func (builder *defaultDocumentBuilder) AppendTitleBlock(ctx context.Context, titleString string) error {
	textElements := []*larkdocx.TextElement{
		larkdocx.NewTextElementBuilder().TextRun(
			larkdocx.NewTextRunBuilder().Content(titleString).Build(),
		).Build(),
	}

	newBlock := larkdocx.NewBlockBuilder().Heading1(
		larkdocx.NewTextBuilder().Elements(textElements).Build(),
	).Build()

	builder.blocks = append(builder.blocks, newBlock)

	return nil
}

func (builder *defaultDocumentBuilder) AppendPlainTextBlock(ctx context.Context, text string) error {
	textElements := []*larkdocx.TextElement{
		larkdocx.NewTextElementBuilder().TextRun(
			larkdocx.NewTextRunBuilder().Content(text).Build(),
		).Build(),
	}

	newBlock := larkdocx.NewBlockBuilder().Text(
		larkdocx.NewTextBuilder().Elements(textElements).Build(),
	).Build()

	builder.blocks = append(builder.blocks, newBlock)

	return nil
}

// Currently same as AppendPlainTextBlock
func (builder *defaultDocumentBuilder) AppendMarkdownTextBlock(ctx context.Context, markdownText string) error {
	textElements := []*larkdocx.TextElement{
		larkdocx.NewTextElementBuilder().TextRun(
			larkdocx.NewTextRunBuilder().Content(markdownText).Build(),
		).Build(),
	}

	newBlock := larkdocx.NewBlockBuilder().Text(
		larkdocx.NewTextBuilder().Elements(textElements).Build(),
	).Build()

	builder.blocks = append(builder.blocks, newBlock)

	return nil
}

func (builder *defaultDocumentBuilder) AppendTextTable(ctx context.Context, rows uint, cols uint, texts []string) error {
	return nil
}

// WARNING: this api can be slow
func (builder *defaultDocumentBuilder) Create(ctx context.Context) (documentId string, err error) {
	req := larkdocx.NewCreateDocumentReqBuilder().
		Body(larkdocx.NewCreateDocumentReqBodyBuilder().
			Title(builder.title).
			Build()).
		Build()
	resp, err := builder.larkcli.Docx.V1.Document.Create(context.Background(), req)

	if err != nil {
		return "", err
	}

	if !resp.Success() {
		return "", fmt.Errorf("create document request failed, code: %d, message: %s, requestId: %s", resp.Code, resp.Msg, resp.RequestId())
	}

	documentId = *resp.Data.Document.DocumentId

	// HACK: sleep to avoid frequency control
	time.Sleep(time.Second)

	blockReq := larkdocx.NewCreateDocumentBlockChildrenReqBuilder().
		DocumentId(documentId).
		BlockId(documentId).
		DocumentRevisionId(-1).
		Body(larkdocx.NewCreateDocumentBlockChildrenReqBodyBuilder().
			Children(builder.blocks).
			Index(-1).
			Build()).
		Build()
	blockResp, err := builder.larkcli.Docx.V1.DocumentBlockChildren.Create(ctx, blockReq)

	if err != nil {
		return "", err
	}

	if !blockResp.Success() {
		return "", fmt.Errorf("create document blocks request failed, code: %d, message: %s, requestId: %s, blocks: %v", blockResp.Code, blockResp.Msg, blockResp.RequestId(), builder.blocks)
	}

	return documentId, err
}

func (dc *docCli) NewDefaultDocumentBuilder(docTitle string) (DocumentBuilder, error) {
	return &defaultDocumentBuilder{
		larkcli:        dc.larkCli,
		title:          docTitle,
		reqBodyBuilder: larkdocx.NewCreateDocumentReqBodyBuilder(),
	}, nil
}
