package main

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"github.com/SebastiaanKlippert/go-wkhtmltopdf"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"github.com/google/uuid"
	"google.golang.org/grpc"
	"io/ioutil"
	"log"
	"net"
	"os"
	"sync"
	"time"
	"wkhtmltopdf/server/grpc/pb"
)

func main() {
	grpcListener, err := net.Listen("tcp", ":50003")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	grpcServer := grpc.NewServer()
	pb.RegisterWkhtmltopdfServiceServer(grpcServer, &wkhtmltopdfServer{})
	log.Printf("server listening at %v", grpcListener.Addr())
	if err := grpcServer.Serve(grpcListener); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

type wkhtmltopdfServer struct {
	pb.UnimplementedWkhtmltopdfServiceServer
}

func (s *wkhtmltopdfServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PingResponse, error) {
	return &pb.PingResponse{Status: "success"}, nil
}

func (s *wkhtmltopdfServer) Generate(ctx context.Context, req *pb.GenerateRequest) (*pb.GenerateResponse, error) {
	var files []string
	for _, html := range req.Data {
		tempFile, _ := ioutil.TempFile("/tmp", "label-*.html")
		_, err := tempFile.Write(html)
		if err != nil {
			fmt.Println(err)
			continue
		}

		if err := tempFile.Close(); err != nil {
			fmt.Println(err)
			continue
		}
		files = append(files, tempFile.Name())
	}
	pdf, err := generatePDF(files, req.Title)
	if err != nil {
		fmt.Println(err)
	}
	url, err := uploadPdfToS3(uuid.NewString(), pdf)
	if err != nil {
		fmt.Println(err)
	}
	removeTempFiles(files)
	return &pb.GenerateResponse{
		Url: url,
	}, nil
}

// https://github.com/SebastiaanKlippert/go-wkhtmltopdf
func generatePDF(htmlFiles []string, pdfTitle string) ([]byte, error) {
	if len(htmlFiles) < 1 {
		return nil, errors.New("all of shipments was error, no file to generate")
	}

	wkhtmltopdf.SetPath("wkhtmltopdf")
	pdfGenerator, err := wkhtmltopdf.NewPDFGenerator()
	if err != nil {
		return nil, err
	}

	// Set global options
	pdfGenerator.Title.Set(pdfTitle)
	pdfGenerator.Dpi.Set(96)
	pdfGenerator.PageWidth.Set(100)
	pdfGenerator.PageHeight.Set(180)
	pdfGenerator.MarginTop.Set(0)
	pdfGenerator.MarginBottom.Set(0)
	pdfGenerator.MarginLeft.Set(0)
	pdfGenerator.MarginRight.Set(0)
	pdfGenerator.Orientation.Set(wkhtmltopdf.OrientationPortrait)

	for _, path := range htmlFiles {
		fmt.Println(path)
		pdfGenerator.AddPage(wkhtmltopdf.NewPage(path))
	}

	// Create PDF document in internal buffer
	if err := pdfGenerator.Create(); err != nil {
		return nil, err
	}

	return pdfGenerator.Bytes(), nil
}

func uploadPdfToS3(name string, b []byte) (string, error) {
	id := "AKIATWN4JEU3ES4BERC6"
	secret := "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"
	folder := "tmp"
	bucket := "poogky"
	region := "ap-southeast-1"
	presignedTTL := 10

	filePath := fmt.Sprintf("%s/%s.pdf", folder, name)

	awsSession := session.Must(session.NewSession(&aws.Config{
		Credentials: credentials.NewStaticCredentials(id, secret, ""),
		Region:      aws.String(region),
	}))

	uploader := s3manager.NewUploader(awsSession)
	_, err := uploader.Upload(&s3manager.UploadInput{
		Key:    aws.String(filePath),
		Bucket: aws.String(bucket),
		ACL:    aws.String("private"),
		Body:   bytes.NewReader(b),
	})

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

	awsClient := s3.New(awsSession)
	req, _ := awsClient.GetObjectRequest(&s3.GetObjectInput{
		Key:                        aws.String(filePath),
		Bucket:                     aws.String(bucket),
		ResponseContentType:        aws.String("application/pdf"),
		ResponseContentDisposition: aws.String("inline"),
	})

	ttl := time.Duration(presignedTTL) * time.Minute
	url, err := req.Presign(ttl)
	if err != nil {
		return "", fmt.Errorf("failed to presign GetObjectRequest for key %q: %v", filePath, err)
	}

	return url, nil
}

func removeTempFiles(htmlFiles []string) {
	// Remove temp files
	fmt.Println("Removing temp files")
	wg := sync.WaitGroup{}
	wg.Add(len(htmlFiles))
	for _, path := range htmlFiles {
		path := path
		go func() {
			wg.Done()
			if path != "" {
				if err := os.Remove(path); err != nil {
					log.Println(path, err)
				} else {
					fmt.Println("Removed", path)
				}
			}
		}()
	}
	wg.Wait()
}
