# 월별 매출 시트를 모아 '보고서'(지점별·월별 합계, 품목별 합계)를 만드는 스크립트
# 사용법: python make_report.py sales.xlsx
#   → 같은 폴더에 sales_보고서.xlsx 가 생깁니다. 원본 파일은 바꾸지 않습니다.
# 필요한 것: Python, openpyxl (설치: python -m pip install openpyxl)
#
# 이렇게 동작합니다.
# - 파일 안의 모든 시트를 읽습니다. 이름이 '보고서'인 시트는 건너뜁니다.
# - 머리글은 위에서 5행 안에서 찾습니다. 열 순서가 달라도, 중간에 열이 추가돼도 이름으로 찾습니다.
# - 빈 행, '합계'·'소계'·'총계' 행은 건너뜁니다.
# - 금액이 비었거나 숫자가 아닌 행은 빼고, 몇 행인지 알려 줍니다.
import sys
import time
from pathlib import Path

# ---- 필요하면 여기만 고치세요 ----
REPORT_SHEET = '보고서'                      # 결과 시트 이름(이 이름의 시트는 집계에서 뺍니다)
NEED = ['지점', '품목', '금액']              # 꼭 있어야 하는 열
ALIASES = {'매출액': '금액', '판매금액': '금액', '점포': '지점'}  # 같은 열로 볼 다른 이름
SKIP_WORDS = {'합계', '소계', '총계'}
# --------------------------------

try:
    from openpyxl import Workbook, load_workbook
    from openpyxl.styles import Font, PatternFill
except ImportError:
    print('openpyxl이 설치돼 있지 않습니다. 명령 프롬프트에서 아래 줄을 실행한 뒤 다시 해 보세요.')
    print('  python -m pip install openpyxl')
    sys.exit(1)


def to_number(v):
    """숫자, '12,000' 같은 글자 숫자를 숫자로 바꿉니다. 못 바꾸면 None."""
    if isinstance(v, bool):
        return None
    if isinstance(v, (int, float)):
        return v
    if isinstance(v, str):
        s = v.replace(',', '').replace('원', '').strip()
        try:
            return float(s) if '.' in s else int(s)
        except ValueError:
            return None
    return None


def norm(v):
    s = '' if v is None else str(v).strip()
    return ALIASES.get(s, s)


def find_header(rows):
    """위 5행 안에서 필요한 열 이름이 모두 있는 행을 찾습니다."""
    for i, row in enumerate(rows[:5]):
        names = [norm(v) for v in row]
        if all(n in names for n in NEED):
            return i, {n: names.index(n) for n in NEED}
    return None, None


def main():
    if len(sys.argv) < 2:
        print('사용법: python make_report.py 파일이름.xlsx')
        return 1
    src = Path(sys.argv[1])
    if not src.exists():
        print(f'파일을 찾지 못했습니다: {src}')
        print('파일 이름과 위치를 확인하세요. 띄어쓰기가 있으면 "따옴표"로 감쌉니다.')
        return 1
    if src.suffix.lower() == '.xls':
        print('.xls(엑셀 97-2003) 파일은 읽지 못합니다. 엑셀에서 열어 .xlsx로 다른 이름으로 저장한 뒤 다시 실행하세요.')
        return 1
    start = time.perf_counter()
    try:
        wb = load_workbook(src, read_only=True, data_only=True)
    except Exception as e:  # 손상 파일, 암호 걸린 파일 등
        print(f'파일을 열지 못했습니다: {src.name} ({type(e).__name__})')
        print('엑셀에서 정상적으로 열리는지, 암호·보안(DRM)이 걸려 있지 않은지 확인하세요.')
        return 1

    months = []                     # 시트 이름 순서대로
    by_branch = {}                  # {지점: {시트: 금액}}
    by_product = {}                 # {품목: 금액}
    used = skipped_blank = 0
    notes = []
    for ws in wb.worksheets:
        if ws.title.strip() == REPORT_SHEET:
            continue
        rows = [list(r) for r in ws.iter_rows(values_only=True)]
        if not rows:
            notes.append(f'{ws.title}: 빈 시트라 건너뜀')
            continue
        h, col = find_header(rows)
        if h is None:
            names = [norm(v) for v in (rows[0] if rows else [])]
            lack = [n for n in NEED if n not in names]
            notes.append(f'{ws.title}: 머리글을 찾지 못해 건너뜀 (없는 열: {", ".join(lack)})')
            continue
        months.append(ws.title)
        bad = 0
        for row in rows[h + 1:]:
            row = list(row) + [None] * (len(rows[h]) - len(row))
            if all(v is None or str(v).strip() == '' for v in row):
                skipped_blank += 1
                continue
            if any(str(v).replace(' ', '') in SKIP_WORDS for v in row if v is not None):
                skipped_blank += 1
                continue
            branch = str(row[col['지점']] or '').strip()
            product = str(row[col['품목']] or '').strip()
            amount = to_number(row[col['금액']])
            if not branch or not product or amount is None:
                bad += 1
                continue
            by_branch.setdefault(branch, {}).setdefault(ws.title, 0)
            by_branch[branch][ws.title] += amount
            by_product[product] = by_product.get(product, 0) + amount
            used += 1
        if bad:
            notes.append(f'{ws.title}: 지점·품목·금액이 비었거나 금액이 숫자가 아닌 행 {bad}개를 뺐습니다')
    wb.close()

    if not months:
        print('집계할 시트가 없습니다. 머리글에 지점, 품목, 금액 열이 있는지 확인하세요.')
        for n in notes:
            print('  확인 필요:', n)
        return 1

    out = Workbook()
    ws = out.active
    ws.title = REPORT_SHEET
    bold = Font(bold=True)
    fill = PatternFill('solid', fgColor='DDEBF7')
    money = '#,##0'
    ws.append(['지점'] + months + ['합계'])
    for b in by_branch:
        vals = [by_branch[b].get(m, 0) for m in months]
        ws.append([b] + vals + [sum(vals)])
    col_tot = [sum(by_branch[b].get(m, 0) for b in by_branch) for m in months]
    ws.append(['합계'] + col_tot + [sum(col_tot)])
    last1 = ws.max_row
    ws.append([])
    ws.append(['품목', '금액'])
    head2 = ws.max_row
    for p, v in sorted(by_product.items(), key=lambda x: -x[1]):
        ws.append([p, v])
    for r in (1, last1, head2):
        for c in ws[r]:
            c.font = bold
            if r != last1:
                c.fill = fill
    for row in ws.iter_rows():
        for c in row:
            if isinstance(c.value, (int, float)):
                c.number_format = money
    ws.column_dimensions['A'].width = 12
    for i in range(2, len(months) + 3):
        ws.column_dimensions[ws.cell(1, i).column_letter].width = 16

    dst = src.with_name(f'{src.stem}_보고서.xlsx')
    try:
        out.save(dst)
    except PermissionError:
        print(f'{dst.name} 파일을 저장하지 못했습니다. 엑셀에서 열려 있으면 닫고 다시 실행하세요.')
        return 1
    sec = time.perf_counter() - start
    print(f'시트 {len(months)}개({", ".join(months)}), 집계한 행 {used}개, 건너뛴 빈·합계 행 {skipped_blank}개, {sec:.2f}초 → {dst.name}')
    for n in notes:
        print('  확인 필요:', n)
    return 0


if __name__ == '__main__':
    sys.exit(main())
