/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package checker

import (
	"github.com/apache/answer/internal/base/constant"
	"github.com/apache/answer/pkg/obj"
	"github.com/apache/answer/pkg/uid"
)

const (
	QuestionLinkTypeURL = 1
	QuestionLinkTypeID  = 2
)

type QuestionLink struct {
	LinkType   int
	QuestionID string
	AnswerID   string
}

func GetQuestionLink(content string) []QuestionLink {
	uniqueIDs := make(map[string]struct{})
	var questionLinks []QuestionLink

	// use two pointer to find the link
	left, right := 0, 0
	for right < len(content) {
		// find "/questions/" or "#"
		switch {
		case right+11 < len(content) && content[right:right+11] == "/questions/":
			left = right
			right += 11
			processURL(content, &left, &right, uniqueIDs, &questionLinks)
		case content[right] == '#':
			left = right + 1
			right = left
			processID(content, &left, &right, uniqueIDs, &questionLinks)
		default:
			right++
		}
	}

	return questionLinks
}

func processURL(content string, left, right *int, uniqueIDs map[string]struct{}, questionLinks *[]QuestionLink) {
	for *right < len(content) && (isDigit(content[*right]) || isLetter(content[*right])) {
		*right++
	}
	questionID := content[*left+len("/questions/") : *right]

	answerID := ""
	if *right < len(content) && content[*right] == '/' {
		*left = *right + 1
		*right = *left
		for *right < len(content) && (isDigit(content[*right]) || isLetter(content[*right])) {
			*right++
		}
		answerID = content[*left:*right]
	}

	addUniqueID(questionID, answerID, QuestionLinkTypeURL, uniqueIDs, questionLinks)
}

func processID(content string, left, right *int, uniqueIDs map[string]struct{}, questionLinks *[]QuestionLink) {
	for *right < len(content) && (isDigit(content[*right]) || isLetter(content[*right])) {
		*right++
	}
	id := content[*left:*right]
	addUniqueID(id, "", QuestionLinkTypeID, uniqueIDs, questionLinks)
}

func isDigit(c byte) bool {
	return c >= '0' && c <= '9'
}

func isLetter(c byte) bool {
	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}

func addUniqueID(questionID, answerID string, linkType int, uniqueIDs map[string]struct{}, questionLinks *[]QuestionLink) {
	isAdd := false
	if answerID != "" {
		objectType, err := obj.GetObjectTypeStrByObjectID(uid.DeShortID(answerID))
		if err != nil {
			answerID = ""
		} else if objectType == constant.AnswerObjectType {
			if _, ok := uniqueIDs[answerID]; !ok {
				uniqueIDs[answerID] = struct{}{}
				isAdd = true
			}
		}
	}

	if objectType, err := obj.GetObjectTypeStrByObjectID(uid.DeShortID(questionID)); err == nil {
		if _, ok := uniqueIDs[questionID]; !ok {
			uniqueIDs[questionID] = struct{}{}
			isAdd = true
			if objectType == constant.AnswerObjectType {
				answerID = questionID
				questionID = ""
			}
		}
	}

	if isAdd {
		*questionLinks = append(*questionLinks, QuestionLink{
			LinkType:   linkType,
			QuestionID: questionID,
			AnswerID:   answerID,
		})
	}
}
