package ii

import (
	"fmt"
	"strings"
)

const (
	formTextTemp = `			<div class="col-md-6" %s>
									<div class="row %s">
										<label for="%s" class="col-form-label col-sm-3">%s</label>
										<div class="col-sm-7 mb-3">
											<input type="%s" class="form-control %s" %s id="%s" value="%s" %s %s %s>
											%s%s%s
										</div>
										%s
									</div>
								</div>`
	formTextTempWithUnit = `<label for="%s" class="col-form-label col-sm-1">%s</label>`
	formSelectTempWith   = `		<div class="col-md-6" %s>
									<div class="row %s">
										<label for="%s" class="col-form-label col-sm-3">%s</label>
										<div class="col-sm-7 mb-3">
											<select class="form-control select2 lowCode" data-toggle="select2" id="%s"
													%s %s %s %s %s>
												%s
											</select>
											%s%s%s
										</div>
										%s
									</div>
								</div>`
	formTextareaTemp = `		<div class="col-md-6" %s>
									<div class="row %s">
										<label for="%s" class="col-form-label col-sm-3">%s</label>
										<div class="col-sm-7 mb-3">
											<textarea class="form-control" placeholder="" rows="3" id="%s" %s %s>%s</textarea>
											%s%s%s
										</div>
										%s
									</div>
								</div>`
	formTextTempDateOptions = `{"format":"%s","separator":" ~ ","applyLabel":"确定","cancelLabel":"取消","fromLabel":"从","toLabel":"至","customRangeLabel":"自定义","daysOfWeek":["日","一","二","三","四","五","六"],"monthNames": ["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],"firstDay": 1}`
)

func (f Form) HiddenAttr() string {
	if f.Hidden {
		return "hidden"
	}
	return ""
}

func (f Form) DateCss() string {
	if f.Date != "" {
		return "v-dateRangePicker lowCode"
	}
	return ""
}

func (f Form) DateStyle() string {
	var (
		opt    string
		format = "YYYY-MM-DD"
	)
	switch f.Date {
	case "date":
		opt = `data-single-Date-Picker="true" `
	case "dateTime":
		opt = `data-single-Date-Picker="true" data-time-Picker="true" `
		format = "YYYY-MM-DD HH:mm"
	case "dateTimeSecond":
		opt = `data-single-Date-Picker="true" data-time-Picker="true" data-time-Picker-Seconds="true" `
		format = "YYYY-MM-DD HH:mm:ss"
	case "dateRange":
		opt = `data-single-Date-Picker="false" data-time-Picker="false" `
	case "dateTimeRange":
		opt = `data-single-Date-Picker="false" data-time-Picker="true" `
		format = "YYYY-MM-DD HH:mm"
	case "dateTimeRangeSecond":
		opt = `data-single-Date-Picker="false" data-time-Picker="true" data-time-Picker-Seconds="true" `
		format = "YYYY-MM-DD HH:mm:ss"
	default:
		return ""
	}
	return opt + fmt.Sprintf(`data-locale='%s'`, fmt.Sprintf(formTextTempDateOptions, format))
}

func (f Form) UnitCss() string {
	if f.Unit != "" {
		return "g-3"
	}
	return ""
}
func (f Form) UnitLabel(field *FieldInfo) string {
	if f.Unit != "" {
		return fmt.Sprintf(formTextTempWithUnit, field.Name, f.Unit)
	}
	return ""
}

func (f Form) Required(r bool) string {
	if r {
		return "required"
	}
	return ""
}

func (f Form) ReadDisable() string {
	if f.Disable {
		return "disabled"
	}
	if f.ReadOnly {
		return "readonly"
	}
	return ""
}

func (f Form) Limit(mode string, field *FieldInfo) string {
	switch mode {
	case "text":
		if f.Date != "" {
			return ""
		}
		return fmt.Sprintf(`minlength="%f" maxlength="%f"`, field.Minimum, field.Maximum)
	case "number":
		if field.Minimum == 0 && field.Maximum == 0 {
			return ""
		}
		step := func() string {
			if field.Decimal == 0 {
				return ""
			}
			var step string
			for i := 0; i < field.Decimal; i++ {
				step += "0"
			}
			return "0." + step
		}
		return fmt.Sprintf(`min="%f" max="%f" %s`, field.Minimum, field.Maximum, step())
	case "select":
		return fmt.Sprintf(`data-maximum-Selection-Length="%f"`, field.Maximum)
	default:
		return ""
	}
}

func (f Form) Value(field *FieldInfo) string {
	s, err := field.convertString(field.defaultValue)
	if err != nil {
		return err.Error()
	}
	return s
}

func (f Form) HelpCss() string {
	if f.Help != "" {
		return fmt.Sprintf(`<div class="form-text">%s</div>`, f.Help)
	}
	return ""
}

func (f Form) SelectAjax() string {
	if f.URL != "" {
		return fmt.Sprintf(`data-ajax-url="%s"`, f.URL)
	}
	return ""
}

func (f Form) SelectMulti() string {
	if f.Multiple {
		return `data-allow-clear="true" multiple`
	}
	return ""
}

func (f Form) SelectOption(field *FieldInfo) string {
	opt := strings.Builder{}
	if !field.Required {
		opt.WriteString(`<option></option>`)
	}
	sel := strings.Split(f.Selected, ",")
	for _, enum := range field.Enums {
		if _, ok := f.isSelected(sel, enum); ok {
			opt.WriteString(fmt.Sprintf(`<option value="%v" selected>%v</option>`, enum, enum))
		} else {
			opt.WriteString(fmt.Sprintf(`<option value="%v">%v</option>`, enum, enum))
		}
	}
	return opt.String()
}

func (f Form) isSelected(enums []string, v string) (int, bool) {
	for i, enum := range enums {
		if enum == v {
			return i, true
		}
	}
	return -1, false
}

func (f Form) ValidFeedbackCss() string {
	if f.ValidFeedback == "" {
		f.ValidFeedback = "&nbsp;"
	}
	return fmt.Sprintf(`<div class="valid-feedback">%s</div>`, f.ValidFeedback)
}

func (f Form) InValidFeedbackCss() string {
	if f.InvalidFeedback == "" {
		f.InvalidFeedback = "&nbsp;"
	}
	return fmt.Sprintf(`<div class="invalid-feedback">%s</div>`, f.InvalidFeedback)
}

func (f *FieldInfo) Former() string {
	switch f.Form.Mode {
	case "text", "number", "password":
		return fmt.Sprintf(formTextTemp,
			f.Form.HiddenAttr(),
			f.Form.UnitCss(),
			f.Name, f.Label, f.Form.Mode,
			f.Form.DateCss(),
			f.Form.DateStyle(),
			f.Name,
			f.Form.Value(f),
			f.Form.Limit(f.Form.Mode, f),
			f.Form.Required(f.Required),
			f.Form.ReadDisable(),
			f.Form.HelpCss(),
			f.Form.ValidFeedbackCss(),
			f.Form.InValidFeedbackCss(),
			f.Form.UnitLabel(f),
		)
	case "select":
		return fmt.Sprintf(formSelectTempWith,
			f.Form.HiddenAttr(),
			f.Form.UnitCss(),
			f.Name, f.Label, f.Name,
			f.Form.Limit(f.Form.Mode, f),
			f.Form.SelectAjax(),
			f.Form.Required(f.Required),
			f.Form.ReadDisable(),
			f.Form.SelectMulti(),
			f.Form.SelectOption(f),
			f.Form.HelpCss(),
			f.Form.ValidFeedbackCss(),
			f.Form.InValidFeedbackCss(),
			f.Form.UnitLabel(f),
		)
	case "textarea":
		return fmt.Sprintf(formTextareaTemp,
			f.Form.HiddenAttr(),
			f.Form.UnitCss(),
			f.Name, f.Label, f.Name,
			f.Form.Required(f.Required),
			f.Form.ReadDisable(),
			f.Form.Value(f),
			f.Form.HelpCss(),
			f.Form.ValidFeedbackCss(),
			f.Form.InValidFeedbackCss(),
			f.Form.UnitLabel(f),
		)
	default:
		return ""
	}
}
