package me.qwq.doghouse.flexmark.ext.fancyquote;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import org.jetbrains.annotations.NotNull;

import com.vladsch.flexmark.util.ast.Block;
import com.vladsch.flexmark.util.ast.BlockContent;
import com.vladsch.flexmark.util.ast.BlockQuoteLike;
import com.vladsch.flexmark.util.ast.KeepTrailingBlankLineContainer;
import com.vladsch.flexmark.util.misc.CharPredicate;
import com.vladsch.flexmark.util.sequence.BasedSequence;

import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Setter;

@Data
@EqualsAndHashCode(callSuper=false)
public class FancyQuote extends Block implements BlockQuoteLike, KeepTrailingBlankLineContainer {

    private static final Pattern TAG_PATTERN = Pattern.compile("^[a-zA-Z][a-zA-Z0-9]*$");
    private static final Pattern CLASS_PATTERN = Pattern.compile("^[a-zA-Z][a-zA-Z0-9\\-]*$");
    
    private BasedSequence openingMarker = BasedSequence.NULL;
    private BasedSequence info = BasedSequence.NULL;
    
    @Setter(AccessLevel.PROTECTED)
    private BasedSequence tag = BasedSequence.NULL;
    
    @Setter(AccessLevel.PROTECTED)
    private Set<BasedSequence> classes = new LinkedHashSet<>();
    
    private BasedSequence attributes = BasedSequence.NULL;
    private BasedSequence closingMarker = BasedSequence.NULL;
    
    @Override
    public void getAstExtra(@NotNull StringBuilder out) {
        BasedSequence content = getContentChars();
        int lines = getContentLines().size();
        segmentSpanChars(out, openingMarker, "open");
        segmentSpanChars(out, info, "info");
        segmentSpanChars(out, attributes, "attributes");
        segmentSpan(out, content, "content");
        out.append(" lines[").append(lines).append("]");
        segmentSpanChars(out, closingMarker, "close");
    }
    
    @Override
    public @NotNull BasedSequence[] getSegments() {
        return new BasedSequence[] { openingMarker, info, attributes, getContentChars(), closingMarker };
    }
    
    public void setInfo(BasedSequence info) {
        if (info == null) return;
        this.info = info;
        if (info.isNotNull() && !info.isBlank()) {
            BasedSequence[] parts = info.split(".");
            if (TAG_PATTERN.matcher(parts[0]).matches()) {
                tag = parts[0];
            }
            
            for (int i = 1; i < parts.length; i++) {
                if (CLASS_PATTERN.matcher(parts[i]).matches()) {
                    classes.add(parts[i]);
                }
            }
        }
    }
    
    public void addClass(CharSequence clazz) {
        if (CLASS_PATTERN.matcher(clazz).matches()) {
            classes.add(BasedSequence.of(clazz));
        }
    }
    
    /**
     * 获取 FancyQuote 的 tag 类型
     * <p>如 `:::div.info` 会匹配到 `div`，未匹配则返回 defaultSequence</p>
     */
    public CharSequence getTag(CharSequence defaultSequence) {
        if (tag.isNotNull() && !tag.isBlank()) {
            return tag;
        }
        return defaultSequence;
    }
    
    /**
     * 获取 FancyQuote class attribute
     * <p>如 `:::div.info.xx` 会返回 `info xx`</p>
     * @param prefix
     * @return
     */
    public BasedSequence getClasses() {
        if (classes.isEmpty()) return BasedSequence.EMPTY;
        return BasedSequence.of(String.join(" ", classes));
    }

    public BasedSequence getInfoDelimitedByAny(CharPredicate delimiters) {
        BasedSequence language = BasedSequence.NULL;
        if (info.isNotNull() && !info.isBlank()) {
            int delimiter = info.indexOfAny(delimiters);
            if (delimiter == -1) {
                language = info;
            } else {
                language = info.subSequence(0, delimiter);
            }
        }
        return language;
    }
    
    public FancyQuote() {
    }

    public FancyQuote(BasedSequence chars) {
        super(chars);
    }

    public FancyQuote(BasedSequence chars, List<BasedSequence> segments) {
        super(chars, segments);
    }

    public FancyQuote(BlockContent blockContent) {
        super(blockContent);
    }

}
