# 공통 분석 모듈1. 이건 왠만하면 바꾸지 마세용ㅇㅇ

import os
import warnings

import numpy as np
import pandas as pd
import matplotlib
matplotlib.use("Agg")   # 그림을 창으로 안 띄우고 파일로만 저장
import matplotlib.pyplot as plt
from matplotlib import font_manager
import folium

warnings.filterwarnings("ignore")   # 경고 메시지 너무 많이 떠서 끔

# 폴더 경로 주의!
폴더 = os.path.dirname(os.path.abspath(__file__))   # 파일이 있는 폴더
데이터폴더 = os.path.join(폴더, "data")
결과폴더 = os.path.join(폴더, "output")
os.makedirs(결과폴더, exist_ok=True)   # output 폴더 없으면 만들기!

SEED = 42            # 랜덤 값 고정(돌릴 때마다 결과 같게)
TARGET = "조치원읍"   # 타겟 지역
GRID_M = 250         # 격자 크기 (보통 250m)
MIN_STORES = 8       # 격자 안에 업소가 8개보다 적으면 상권으로 안 봄

# 한글 폰트 설정 (안 하면 깨짐)
폰트목록 = [f.name for f in font_manager.fontManager.ttflist]
if "Noto Sans CJK KR" in 폰트목록:
    plt.rcParams["font.family"] = "Noto Sans CJK KR"
elif "NanumGothic" in 폰트목록:
    plt.rcParams["font.family"] = "NanumGothic"
elif "Malgun Gothic" in 폰트목록:
    plt.rcParams["font.family"] = "Malgun Gothic"   # 윈도우
plt.rcParams["axes.unicode_minus"] = False   # 마이너스 기호 깨짐 방지(?)

# 그래프 색깔 (업종, 군집 구분할 때 순서대로 씀. 색깔은 내맘대로 함ㅇㅇ)
COLORS = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728",
          "#9467bd", "#8c564b", "#e377c2", "#17becf"]

# 결과를 화면에 출력하고 나중에 txt 파일로도 저장하려고 리스트에 모아둠 참고하셔요
결과기록 = []


# 공통 함수
# log: 화면 출력 + 결과기록에 저장(파일로 나와요!)
# 그림저장: 그래프를 output에 저장
# 지도만들기: 배경지도가 깔린 빈 folium 지도
# 다양성: 업종 다양성 지수 (행정동, 법정동은 쓰면 클남)
# 최빈값: 격자의 대표 행정동 정할 때 씀(행!정!동!!!! 제발 법정동 쓰지마)
def log(내용=""):
    print(내용)
    결과기록.append(str(내용))


def 그림저장(파일이름):
    plt.tight_layout()
    plt.savefig(os.path.join(결과폴더, 파일이름), bbox_inches="tight", dpi=120)
    plt.close()


def 지도만들기(위치, 확대):
    # 기본 지도(OSM)가 html 파일로 열면 안 나와서 다른 주소(OSM HOT)를 씀. 왜 이러는지 모르겠음  ## 나도 모름
    m = folium.Map(location=위치, zoom_start=확대, tiles=None)
    folium.TileLayer(
        tiles="https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
        attr="&copy; OpenStreetMap contributors, Tiles style by HOT, hosted by OpenStreetMap France",
        name="배경지도(OpenStreetMap HOT)", max_zoom=19).add_to(m)
    return m


def 다양성(s):
    # 샤논 엔트로피 (업종이 여러 개 골고루 있으면 값이 커짐)
    p = s.value_counts(normalize=True)
    return float(-(p * np.log(p)).sum())


def 최빈값(s):
    # 제일 많이 나온 값
    return s.mode().iloc[0]


# 위도, 경도를 미터로 바꿀 때 기준점 (세종 가운데 중심)
LAT0 = 36.50
LON0 = 127.27


# 1단계 : 데이터 불러오기, 전처리
def 데이터준비():
    # 하는 일- 두 시점 csv 를 읽어서 정리하고, 뒤 단계에서 계속 쓰는 컬럼 제작
    # 돌려주는 값
    #   now: 2026 업소 표
    #   prev: 2025 업소 표
    #   CATS: 업종 대분류 10개 이름 (업소 많은 순). 그래프, 군집 변수 순서를 이걸로 통일
    log("=" * 70)
    log("1. 데이터 불러오기 및 전처리")
    log("=" * 70)

    # 원자료는 컬럼이 39개인데 주소/코드 컬럼은 거의 안 써서 필요한 11개만 읽음.
    # 업종명 공백 문제는 01.py 에서 발견함 (안 지우면 '비알코올'이 두 업종으로 갈라짐. 힘들다아앙)
    컬럼 = ["상가업소번호", "상호명", "지점명", "상권업종대분류명", "상권업종중분류명",
           "상권업종소분류명", "행정동명", "도로명", "층정보", "경도", "위도"]

    now = pd.read_csv(os.path.join(데이터폴더, "sejong_store_202606.csv"), usecols=컬럼, low_memory=False)
    prev = pd.read_csv(os.path.join(데이터폴더, "sejong_store_202506.csv"), usecols=컬럼, low_memory=False)
    log(f"2026-06 업소 수: {len(now):,}개 / 2025-06 업소 수: {len(prev):,}개")

    # 업종 이름 뒤에 공백이 붙은 게 있음 ('비알코올 ') => 이거 없에는거
    for c in ["상권업종대분류명", "상권업종중분류명", "상권업종소분류명", "행정동명"]:
        now[c] = now[c].astype(str).str.strip()
        prev[c] = prev[c].astype(str).str.strip()

    # 결측치, 중복 확인
    결측수 = now[["경도", "위도"]].isna().any(axis=1).sum()
    중복수 = now["상가업소번호"].duplicated().sum()
    log(f"좌표 결측: {결측수}건, 업소번호 중복: {중복수}건")
    now = now.dropna(subset=["경도", "위도"])
    now = now.drop_duplicates("상가업소번호")

    # 분석용 컬럼 만들기
    # 도로,권역,1층여부,체인은 원본에 없어서 직접 만듬
    # 이 컬럼들이 이따 골목vs대로 비교, 원도심vs신도심 비교애 쓰임

    # 도로명에서 마지막 단어만 쓰기 (예: '세종특별자치시 세종특별자치시 충현로' -> '충현로')
    now["도로"] = now["도로명"].astype(str).str.split().str[-1]
    prev["도로"] = prev["도로명"].astype(str).str.split().str[-1]

    # 읍,면이면 원도심/동이면 신도심(예외는 아마 없을듯)
    권역목록 = []
    for 동 in now["행정동명"]:
        if 동.endswith("읍") or 동.endswith("면"):
            권역목록.append("읍면(원도심)")
        else:
            권역목록.append("동(신도심)")
    now["권역"] = 권역목록

    # 1층이면 1, 다른 층이면 0, 층정보가 비어 있으면 -1(이래야 나중에 변환 쉬움)
    now["층"] = now["층정보"].astype(str).str.strip()

    def 일층확인(층):
        if 층 == "1":
            return 1
        elif 층 == "nan":
            return -1
        else:
            return 0

    now["1층여부"] = now["층"].apply(일층확인)

    # 체인점 여부(따로 데이터가 없어서 지점명이 있거나 같은 이름 가게가 3개 이상이면 체인으로 보기로 함. 근데 체인점이 2개 이하면 체인으로 안볼테니까 문제가 있을듯)
    이름개수 = now["상호명"].value_counts()
    now["체인"] = ((now["지점명"].notna()) | (now["상호명"].map(이름개수) >= 3)).astype(int)

    # 1년 전 비교
    # 같은 가게는 두 시점에서 상가업소번호가 같다는 점을 이용
    # 주의!!!!!!!!!!!!!!: 번호만 바뀐 가게도 일부 있어서(약 285건, 탐색과정/02) 소멸이 쪼끔? 크게 잡힐 수 있음
    # 작년에 없던 업소번호 -> 신규 / 작년에 있었는데 올해 없는 업소번호 -> 소멸(폐업이나 이전)
    작년번호 = set(prev["상가업소번호"])
    올해번호 = set(now["상가업소번호"])
    now["신규"] = (~now["상가업소번호"].isin(작년번호)).astype(int)
    prev["소멸"] = (~prev["상가업소번호"].isin(올해번호)).astype(int)
    log(f"1년간 유지 {len(작년번호 & 올해번호):,} / 소멸 {prev['소멸'].sum():,} / 신규 {now['신규'].sum():,}")
    log(f"전체 1년 소멸률 {prev['소멸'].mean():.1%}, 신규 비율(현재 업소 중) {now['신규'].mean():.1%}")

    # 좌표 변환
    # 250m 격자, DBSCAN 60m, 주변 100m 업소 수 같은 걸 계산하려면 미터 단위가 필요함
    # 세종 중심(LAT0, LON0 은 위쪽 설정 부분) 기준으로 대충 계산
    now["x"] = (now["경도"] - LON0) * 111320 * np.cos(np.radians(LAT0))
    now["y"] = (now["위도"] - LAT0) * 110540
    prev["x"] = (prev["경도"] - LON0) * 111320 * np.cos(np.radians(LAT0))
    prev["y"] = (prev["위도"] - LAT0) * 110540

    # 업종 대분류 10개 (많은 순서)
    CATS = now["상권업종대분류명"].value_counts().index.tolist()

    #끝! 모듈 한참 남음 ㅠㅠ
    return now, prev, CATS
