import { computed, inject, provide, ref, shallowRef, type ComputedRef, type Ref } from "vue";
import { DefaultDarkTheme, DefaultTheme } from "./Theme";
import type { DynamicVarType } from "./ThemeTools";
import { ObjectUtils } from "@imengyu/imengyu-utils";

export const ThemeKey = Symbol("NanaThemeKey");

let defaultSizeUnit = 'rpx';

/**
 * 配置默认的尺寸单位
 * @param unit 
 */
export function configDefaultSizeUnit(unit: string) {
  defaultSizeUnit = unit;
}

export type ViewStyle = Record<string, any>
export type TextStyle = Record<string, any>

export interface ThemeConfig {
  varOverrides: Record<string, any>,
  colorConfigs: Record<string, Record<string, string>>,
  textConfigs: Record<string, Record<string, string|object>>,
}

function popInjectTheme() : ThemeConfig {
  return inject<Ref<ThemeConfig>>(ThemeKey)?.value ?? DefaultTheme;
}

/**
 * 在PropDefault回调中使用主题默认值的函数
 * @param key 
 * @param defaultValue 
 * @returns 
 */
export function propGetThemeVar<T>(key: string, defaultValue?: T) : T {
  const theme = popInjectTheme();
  return theme?.varOverrides[key] ?? defaultValue;
}
/**
 * 在PropDefault回调中使用主题默认值的函数(回调)
 * @param cb 回调函数，参数为获取主题变量的函数和主题配置
 * @returns 回调函数的返回值
 */
export function propGetThemeVar2(cb: (getVar: (key: string, defaultValue: any) => any, theme: ThemeConfig) => any) {
  const theme = popInjectTheme();
  return cb((key, defaultValue) => getVar(theme, key, defaultValue), theme);
}


export function provideSomeThemeColor(record: Record<string, any>) {
 const topTheme = inject<Ref<ThemeConfig>>(ThemeKey);
  const newTheme = computed(() => {
    const topThemeValue = topTheme?.value ?? DefaultTheme;
    const v = {
      ...topThemeValue,
      colorConfigs: {
        ...topThemeValue.colorConfigs,
        ...record
      }
    } as ThemeConfig;
    return v;
  });
  provide(ThemeKey, newTheme);
}
export function provideSomeThemeVar(record: Record<string, any>) {
  const topTheme = inject<Ref<ThemeConfig>>(ThemeKey);
  const newTheme = computed(() => {
    const topThemeValue = topTheme?.value ?? DefaultTheme;
    const v = {
      ...topThemeValue,
      varOverrides: {
        ...topThemeValue.varOverrides,
        ...record
      }
    } as ThemeConfig;
    return v;
  });
  provide(ThemeKey, newTheme);
}

export function getVar<T>(theme : ThemeConfig, key: string, defaultValue: T) : T {
  return theme.varOverrides[key] ?? defaultValue;
} 

/**
 * 主题的组合代码
 */
export function useTheme() {
  const topTheme = inject<Ref<ThemeConfig>>(ThemeKey);
  const theme = computed(() => topTheme?.value ?? DefaultTheme);

  function resolveThemeSize(inValue?: string|number, defaultValue?: string|number) : string|undefined {    
    const preResolve = resolveSize(inValue);
    if (preResolve !== undefined)
      return preResolve;    
    if (defaultValue && !isRealSize(defaultValue))
      defaultValue = resolveThemeSize(defaultValue);
    if (inValue === undefined)
      inValue = defaultValue;
    if (inValue === undefined)
      return undefined;
    if (typeof inValue === 'string') {
      const v = getSize(inValue, undefined);
      if (v === undefined)
        inValue = defaultValue;
      else
        inValue = v;
    }
    return resolveSize(inValue);
  }
  function resolveThemeColor(inValue?: string, defaultValue?: string) : string|undefined {
    if (inValue === 'transparent')
      return inValue;
    if (inValue === undefined)
      inValue = defaultValue;
    else
      defaultValue = resolveThemeColor(defaultValue);
    if (inValue === undefined)
      return undefined;
    if (inValue.startsWith('#') || inValue.startsWith('rgb'))
      return inValue;     
    return getColor(inValue, defaultValue);
  }
  function resolveThemeSizes<T extends Record<string, string|number>>(defaults: T) {
    const result : T = {} as T;
    for (let key in defaults) 
      result[key] = resolveThemeSize(key, defaults[key]) as any;
    return result;
  }
  function resolveThemeColors<T extends Record<string, string>>(defaults: T) {
    const result : T = {} as T;
    for (let key in defaults)
      result[key] = resolveThemeColor(key, defaults[key]) as any;
    return result;
  }

  function getColor(key: string, defaultValue?: string) {
    if (key === undefined)
      return defaultValue;
    let type = '';
    let keyResolve = getVar(key, key);
    if (typeof keyResolve === 'string')
      key = keyResolve;
    if (key.includes('.'))
      [type, key] = key.split('.');
    let group = theme.value.colorConfigs[type || 'default'];
    if (!group) 
      group = theme.value.colorConfigs['default'];
    return group?.[key] ?? defaultValue;
  }
  function getSize(key: string, defaultValue?: string|number) {
    if (key === undefined)
      return defaultValue;
    let type = '';
    if (key.includes('.'))
      [type, key] = key.split('.');
    let v: any = undefined;
    if (type) {
      const group = theme.value.varOverrides[type];
      v = group?.[key];
    } else
      v = theme.value.varOverrides[key];
    return resolveSize(v ?? defaultValue);
  }
  function getText(key: string, defaultValue?: Record<string, string>) {
    return theme.value.textConfigs[key]?? defaultValue;
  }
  function getVar<T>(key: string, defaultValue?: T) : T {
    let rs = undefined;
    let type = '';
    if (key.includes('.'))
      [type, key] = key.split('.');
    if (type) {
      const group = theme.value.varOverrides[type];
      rs = group?.[key];
    } else
      rs = theme.value.varOverrides[key];
    return rs ?? defaultValue;
  } 

  function getVars<T extends Record<string, string|number|boolean>>(defaults: T) {
    const result : T = {} as T;
    for (let key in defaults) {
      result[key] = getVar(key, defaults[key]);
    }
    return result;
  }
  function getColors<T extends Record<string, string|undefined>>(defaults: T) {
    const result : T = {} as T;
    for (let key in defaults) {
      result[key] = getColor(key, defaults[key]) as any;
    }
    return result;
  }

  function useThemeStyle(style: Record<string, any>) {
    const mapDynamicVars = new Map<string, DynamicVarType>();
    for (const key in style) {
      const v = style[key];
      if (typeof v === 'object' && typeof v['type'] === 'string')
      mapDynamicVars.set(key, v)
    }
    return computed(() => {
      for (const [key, v] of mapDynamicVars) {
        switch (v.type) {
          case 'var': style[key] = getVar(v.name, v.defaultValue); break;
          case 'color': style[key] = resolveThemeColor(v.name, v.defaultValue); break;
          case 'size': style[key] = resolveThemeSize(v.name, v.defaultValue); break;
          case 'size2': style[key] = `${resolveThemeSize(v.name, v.defaultValue)} ${resolveThemeSize(v.name2, v.defaultValue2)}`; break;
        }
      }
      return style;
    })
  }
  function useThemeStyles<T extends Record<string, Record<string, any>>>
    (style: Record<string, Record<string, any>>) : {
      [key in keyof T]: ComputedRef<Record<string, any>>
    }
  {
    const result = {} as Record<string, Record<string, any>>;
    for (const key in style) 
      result[key] = useThemeStyle(style[key]);
    return result as any;
  }

  return {
    theme,
    resolveThemeSizes,
    resolveThemeSize,
    resolveThemeColors,
    resolveThemeColor,
    resolveSize,
    getSize,
    getVar,
    getVars,
    getText,
    getColor,
    getColors,
    useThemeStyle,
    useThemeStyles,
  }
}

function isNumbrSize(inValue: string|number|undefined) {
  if (inValue == undefined)
    return false;
  return typeof inValue === 'number' || !isNaN(Number(inValue));
}
function isRealSize(inValue: string|number|undefined) {
  if (inValue == undefined)
    return false;
  return typeof inValue === 'number' || 
    (typeof inValue === 'string' && (
      inValue.startsWith('calc(') || inValue.endsWith('px') || inValue.endsWith('%') || 
      inValue.endsWith('em') || inValue.endsWith('vh') || inValue.endsWith('vw')
    ));
}
/**
 * 数字单位的处理
 * @param inValue 
 * @param theme 
 * @param themeType 
 * @returns 
 */
export function resolveSize(inValue: string|number|undefined) : string|undefined {
  if (inValue == undefined)
    return undefined;
  if (isNumbrSize(inValue))
    return `${inValue}${defaultSizeUnit}`;
  if (isRealSize(inValue as string))
    return inValue as string;
  if (inValue == 'fill')
    return '100%';
  if (inValue == 'half' || inValue == '1/2')
    return '50%';
  if (inValue == '1/3')
    return '33%';
  if (inValue == '1/4')
    return '25%';
  return undefined;
}

export type ThemePaddingMarginProp = number | number[] | ThemePaddingMargin

export interface ThemePaddingMargin {
  l?: number,
  r?: number,
  t?: number,
  b?: number,
}

/**
 * 配置主题变量，建议在App.vue中调用
 * @param cb 回调函数，参数为默认主题配置，返回值为新的主题配置
 * @returns 主题配置对象，返回currentTheme为当前主题，可以修改为其他主题对象，但请注意主题对象为shallowRef，
 * 直接修改主题对象的属性不会触发主题更新，需要调用currentTheme.value = newTheme新的对象来更新主题。
 */
export function configTheme(
  /** 
   * 是否自动匹配系统深色主题
   * @default true
   */
  autoMatchSystemDark?: boolean, 
  /** 
   * 回调函数，用于配置修改主题，参数为默认主题配置，返回值为新的主题配置
   * @default undefined
   * @returns 可以返回新的主题对象或者是传入的对象，第一个为亮色主题，第二个为深色主题
   */
  cb?: (defaultTheme: ThemeConfig, defaultDarkTheme: ThemeConfig) => [ThemeConfig,ThemeConfig]
) {
  let defaultTheme = ObjectUtils.clone(DefaultTheme);
  let defaultDarkTheme = ObjectUtils.clone(DefaultDarkTheme);
  
  const [theme, darkTheme] = cb?.(defaultTheme, defaultDarkTheme) ?? [defaultTheme, defaultDarkTheme];
  const currentSystemDark = ref(autoMatchSystemDark !== false && uni.getSystemInfoSync().theme === 'dark');
  const currentTheme = shallowRef(currentSystemDark.value ? darkTheme : theme);

  provide(ThemeKey, currentTheme);

  if (autoMatchSystemDark !== false) {
    // 监听系统主题变化
    uni.onThemeChange((res) => {
      currentSystemDark.value = res.theme === 'dark';
      currentTheme.value = currentSystemDark.value ? darkTheme : theme;
    });
  }

  return {
    currentTheme,
  }
}
/**
 * 克隆默认主题，用于自定义主题
 * @param cb 回调函数，参数为默认主题配置，返回值为新的主题配置
 * @param dark 是否为深色主题，默认false为亮色主题
 * @returns 新的主题配置对象
 */
export function cloneTheme(dark?: boolean, cb?: (defaultTheme: ThemeConfig) => ThemeConfig) {
  return cb?.(ObjectUtils.clone(dark ? DefaultDarkTheme : DefaultTheme)) ?? ObjectUtils.clone(dark ? DefaultDarkTheme : DefaultTheme);
}
